* derivation. The group elements U and Z are the cryptographic inputs
* for the derivation.
*
- * Basically all we do is compute H(what || U || Z).
+ * Basically all we do is compute H(what || U || V || Z).
*/
-static int derive(struct kinfo *k, ge *U, ge *Z,
+static int derive(struct kinfo *k, ge *U, ge *V, ge *Z,
const char *what, const char *name, const octet *ksz,
octet **kk, size_t *n)
{
buf_init(&b, obuf, sizeof(obuf));
buf_put(&b, "udpkey-", 7);
buf_putstrz(&b, what);
- G_TORAW(k->g, &b, U);
+ if (U) G_TORAW(k->g, &b, U);
+ if (V) G_TORAW(k->g, &b, V);
G_TORAW(k->g, &b, Z);
if (BBAD(&b)) {
complain(LOG_ERR, "overflow while deriving key (prepare preimage)!");
}
#endif
+/* Derive the symmetric keys for Diffie--Hellman-based symmetric crypto,
+ * given (optional) sender public key U, (optional) recipient public key V,
+ * and secret Z, constructing cipher and mac objects for the actual work.
+ *
+ * Your protocol must be consistent about whether it sends U and/or V: the
+ * formatting is ambiguous if you sometimes use one and sometimes the other.
+ */
+static void dh_derive_keys(struct kinfo *k, ge *U, ge *V, ge *Z,
+ gcipher **cc, gmac **mm)
+{
+ octet *kk;
+ size_t ksz;
+
+ derive(k, U, V, Z, "cipher", k->cc->name, k->cc->keysz, &kk, &ksz);
+ *cc = GC_INIT(k->cc, kk, ksz);
+ derive(k, U, V, Z, "mac", k->mc->name, k->mc->keysz, &kk, &ksz);
+ *mm = GM_KEY(k->mc, kk, ksz);
+}
+
+/* Prepare for an IES encryption. Given a public key X, prepare a clue R and
+ * secret Z for later.
+ */
+static void ies_enc_prepare(struct kinfo *k, ge *X, ge *R, ge *Z)
+{
+ mp *r = mprand_range(MP_NEW, k->g->r, &rand_global, 0);
+ G_EXP(k->g, R, k->g->g, r);
+ G_EXP(k->g, Z, X, r);
+ D( debug_mp("r", r); debug_ge("R", k.g, R); )
+ MP_DROP(r);
+}
+
+/* Actually perform an IES encryption. Given a prepared clue R and secret Z,
+ * and an SZ-byte plaintext message P, write the ciphertext to B. Note that
+ * the clue itself is /not/ written to B: you must do that yourself.
+ */
+static int ies_enc_perform(struct kinfo *k, buf *b, ge *R, ge *Z,
+ const void *p, size_t sz)
+{
+ octet *t, *tt, *ct;
+ gcipher *c;
+ gmac *m;
+ ghash *h;
+
+ if ((t = buf_get(b, k->tagsz)) == 0 ||
+ (ct = buf_get(b, sz)) == 0) {
+ complain(LOG_ERR, "overflow while writing ciphertext");
+ return (-1);
+ }
+
+ dh_derive_keys(k, R, 0, Z, &c, &m);
+ GC_ENCRYPT(c, p, ct, sz);
+ h = GM_INIT(m);
+ GH_HASH(h, ct, sz);
+ tt = GH_DONE(h, 0);
+ memcpy(t, tt, k->tagsz);
+
+ GC_DESTROY(c);
+ GM_DESTROY(m);
+ GH_DESTROY(h);
+}
+
/*----- Protocol summary --------------------------------------------------*
*
* There are two protocol versions. The original version works as follows.
*
* * 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
+ * ge W masked IES clue: W = R - Y = r P - v U
+ * iesct[*](H(R, r X))
+ * mem[*] S secret
*
* The new version provides forward secrecy, which involves additional flows.
*
- * * Greeting
+ * * Request
* u8 0 marker byte for new protocol
* u8 1 packet type
* mem8 KEYTAG wanted secret tag
+ * ge U public vector: U = u P
*
* * Challenge
* u8 17 packet type
- * u32 REF server's reference
- * ge R public DLIES vector: R = r P
- * ge W masked DH vector: W = V - Y = v P - r X
+ * ge R IES clue
+ * iesct[*](H(R, r X))
+ * ge U client's public vector (confirms receipt)
+ * ge V public vector: V = v P
+ * mem[*] a nonce
*
* * Response
* u8 0 marker byte for new protocol
* u8 2 packet type
- * mem8 KEYTAG wanted secret tag
- * u32 REF reference from challenge
- * ge U public DH vector
- * mem[HASHSZ] H0 hash; H0||H1 = H(U, V, Z), where Z = v U
+ * mem[*] a server's nonce (confirm receipt of V)
*
- * * Reply
+ * * Answer
* u8 18 packet type
- * mem[TAGSZ] TAG MAC tag on ciphertext
- * mem[KSZ] CT secret, encrypted with H1
+ * iesct[*](H(U, V, Z))
+ * mem[*] S secret
*/
-#define FWS_GREET 0x01
-#define FWS_CHALL 0x11
+#define FWS_REQ 0x01
+#define FWS_CHAL 0x11
#define FWS_RESP 0x02
-#define FWS_REPLY 0x12
+#define FWS_ANS 0x12
/*----- Listening for requests --------------------------------------------*/
* Secrets are kept in a linked list, ordered by expiry time. At any given
* time there is at most one unexpired secret (because we only make a new one
* when the old one expires).
+ *
+ * The nonce has the form ...
*/
struct secret {
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;
+ mp *v = MP_NEW;
+ struct kinfo k;
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. */
* 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);
+ ies_enc_prepare(&k, k.X, R, Z);
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. */
+ /* Write the ciphertext. */
rc = 0;
if (G_TOBUF(k.g, bout, V) ||
- G_TOBUF(k.g, bout, W))
+ G_TOBUF(k.g, bout, W) ||
+ ies_enc_perform(&k, bout, R, Z, ky->k->u.k.k, ky->k->u.k.sz))
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 (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);
D( debug_ge("R", s->k.g, R);
debug_ge("Y", s->k.g, Y);
debug_ge("Z", s->k.g, Z); )
- derive(&s->k, R, Z, "cipher", s->k.cc->name, s->k.cc->keysz, &kk, &ksz);
+ derive(&s->k, R, 0, Z, "cipher", s->k.cc->name, s->k.cc->keysz, &kk, &ksz);
c = GC_INIT(s->k.cc, kk, ksz);
- derive(&s->k, R, Z, "mac", s->k.mc->name, s->k.mc->keysz, &kk, &ksz);
+ derive(&s->k, R, 0, Z, "mac", s->k.mc->name, s->k.mc->keysz, &kk, &ksz);
m = GM_KEY(s->k.mc, kk, ksz);
/* Find where the MAC tag is. */