Skip to content

Commit

Permalink
channeld: use pointer for shared secret.
Browse files Browse the repository at this point in the history
It's more natural than using a zero-secret when something goes wrong.

Also note that the HSM will actually kill the connection if the ECDH
fails, which is fortunately statistically unlikely.

Signed-off-by: Rusty Russell <[email protected]>
  • Loading branch information
rustyrussell authored and cdecker committed Jan 8, 2019
1 parent 7e01efb commit 66de6b8
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 52 deletions.
30 changes: 15 additions & 15 deletions channeld/channeld.c
Original file line number Diff line number Diff line change
Expand Up @@ -527,29 +527,26 @@ static void handle_peer_announcement_signatures(struct peer *peer, const u8 *msg
channel_announcement_negotiate(peer);
}

static bool get_shared_secret(const struct htlc *htlc,
struct secret *shared_secret)
static struct secret *get_shared_secret(const tal_t *ctx,
const struct htlc *htlc)
{
struct pubkey ephemeral;
struct onionpacket *op;
struct secret *secret = tal(ctx, struct secret);
const u8 *msg;

/* We unwrap the onion now. */
op = parse_onionpacket(tmpctx, htlc->routing, TOTAL_PACKET_SIZE);
if (!op) {
/* Return an invalid shared secret. */
memset(shared_secret, 0, sizeof(*shared_secret));
return false;
}
if (!op)
return tal_free(secret);

/* Because wire takes struct pubkey. */
ephemeral.pubkey = op->ephemeralkey;
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, &ephemeral));
if (!fromwire_hsm_ecdh_resp(msg, shared_secret))
if (!fromwire_hsm_ecdh_resp(msg, secret))
status_failed(STATUS_FAIL_HSM_IO, "Reading ecdh response");

/* Gives all-zero shares_secret if it was invalid. */
return !memeqzero(shared_secret, sizeof(*shared_secret));
return secret;
}

static void handle_peer_add_htlc(struct peer *peer, const u8 *msg)
Expand Down Expand Up @@ -581,8 +578,7 @@ static void handle_peer_add_htlc(struct peer *peer, const u8 *msg)

/* If this is wrong, we don't complain yet; when it's confirmed we'll
* send it to the master which handles all HTLC failures. */
htlc->shared_secret = tal(htlc, struct secret);
get_shared_secret(htlc, htlc->shared_secret);
htlc->shared_secret = get_shared_secret(htlc, htlc);
}

static void handle_peer_feechange(struct peer *peer, const u8 *msg)
Expand Down Expand Up @@ -1215,7 +1211,12 @@ static u8 *got_commitsig_msg(const tal_t *ctx,
memcpy(a->onion_routing_packet,
htlc->routing,
sizeof(a->onion_routing_packet));
*s = *htlc->shared_secret;
/* Invalid shared secret gets set to all-zero: our
* code generator can't make arrays of optional values */
if (!htlc->shared_secret)
memset(s, 0, sizeof(*s));
else
*s = *htlc->shared_secret;
} else if (htlc->state == RCVD_REMOVE_COMMIT) {
if (htlc->r) {
struct fulfilled_htlc *f;
Expand Down Expand Up @@ -2589,8 +2590,7 @@ static void init_shared_secrets(struct channel *channel,
continue;

htlc = channel_get_htlc(channel, REMOTE, htlcs[i].id);
htlc->shared_secret = tal(htlc, struct secret);
get_shared_secret(htlc, htlc->shared_secret);
htlc->shared_secret = get_shared_secret(htlc, htlc);
}
}

Expand Down
18 changes: 12 additions & 6 deletions connectd/connectd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1445,18 +1445,24 @@ static struct io_plan *recv_req(struct io_conn *conn,
* knowledge of the HSM, but also at one stage I made a hacky gossip vampire
* tool which used the handshake code, so it's nice to keep that
* standalone. */
bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point)
struct secret *hsm_do_ecdh(const tal_t *ctx, const struct pubkey *point)
{
u8 *req = towire_hsm_ecdh_req(tmpctx, point), *resp;
struct secret *secret = tal(ctx, struct secret);

if (!wire_sync_write(HSM_FD, req))
return false;
return tal_free(secret);
resp = wire_sync_read(req, HSM_FD);
if (!resp)
return false;
if (!fromwire_hsm_ecdh_resp(resp, ss))
return false;
return true;
return tal_free(secret);

/* Note: hsmd will actually hang up on us if it can't ECDH: that implies
* that our node private key is invalid, and we shouldn't have made
* it this far. */
if (!fromwire_hsm_ecdh_resp(resp, secret))
return tal_free(secret);

return secret;
}

/*~ UNUSED is defined to an __attribute__ for GCC; at one stage we tried to use
Expand Down
41 changes: 22 additions & 19 deletions connectd/handshake.c
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ struct handshake {
struct secret temp_k;
struct sha256 h;
struct keypair e;
struct secret ss;
struct secret *ss;

/* Used between the Acts */
struct pubkey re;
Expand Down Expand Up @@ -473,18 +473,19 @@ static struct io_plan *act_three_initiator(struct io_conn *conn,
* * where `re` is the ephemeral public key of the responder
*
*/
if (!hsm_do_ecdh(&h->ss, &h->re))
h->ss = hsm_do_ecdh(h, &h->re);
if (!h->ss)
return handshake_failed(conn, h);

SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, &h->ss, sizeof(h->ss)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss)));

/* BOLT #8:
*
* 4. `ck, temp_k3 = HKDF(ck, ss)`
* * The final intermediate shared secret is mixed into the running
* chaining key.
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k3=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down Expand Up @@ -549,19 +550,19 @@ static struct io_plan *act_two_initiator2(struct io_conn *conn,
* 5. `ss = ECDH(re, e.priv)`
* * where `re` is the responder's ephemeral public key
*/
if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->re.pubkey,
if (!secp256k1_ecdh(secp256k1_ctx, h->ss->data, &h->re.pubkey,
h->e.priv.secret.data))
return handshake_failed(conn, h);

SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, &h->ss, sizeof(h->ss)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss)));

/* BOLT #8:
*
* 6. `ck, temp_k2 = HKDF(ck, ss)`
* * A new temporary encryption key is generated, which is
* used to generate the authenticating MAC.
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k2=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down Expand Up @@ -639,19 +640,20 @@ static struct io_plan *act_one_initiator(struct io_conn *conn,
* * The initiator performs an ECDH between its newly generated
* ephemeral key and the remote node's static public key.
*/
if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data,
h->ss = tal(h, struct secret);
if (!secp256k1_ecdh(secp256k1_ctx, h->ss->data,
&h->their_id.pubkey, h->e.priv.secret.data))
return handshake_failed(conn, h);

SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss.data, sizeof(h->ss.data)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss->data, sizeof(h->ss->data)));

/* BOLT #8:
*
* 4. `ck, temp_k1 = HKDF(ck, ss)`
* * A new temporary encryption key is generated, which is
* used to generate the authenticating MAC.
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k1=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down Expand Up @@ -740,16 +742,16 @@ static struct io_plan *act_three_responder2(struct io_conn *conn,
* 6. `ss = ECDH(rs, e.priv)`
* * where `e` is the responder's original ephemeral key
*/
if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->their_id.pubkey,
if (!secp256k1_ecdh(secp256k1_ctx, h->ss->data, &h->their_id.pubkey,
h->e.priv.secret.data))
return handshake_failed(conn, h);

SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, &h->ss, sizeof(h->ss)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss)));

/* BOLT #8:
* 7. `ck, temp_k3 = HKDF(ck, ss)`
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k3=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down Expand Up @@ -815,18 +817,18 @@ static struct io_plan *act_two_responder(struct io_conn *conn,
* * where `re` is the ephemeral key of the initiator, which was
* received during Act One
*/
if (!secp256k1_ecdh(secp256k1_ctx, h->ss.data, &h->re.pubkey,
if (!secp256k1_ecdh(secp256k1_ctx, h->ss->data, &h->re.pubkey,
h->e.priv.secret.data))
return handshake_failed(conn, h);
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, &h->ss, sizeof(h->ss)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss)));

/* BOLT #8:
*
* 4. `ck, temp_k2 = HKDF(ck, ss)`
* * A new temporary encryption key is generated, which is
* used to generate the authenticating MAC.
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k2=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down Expand Up @@ -902,18 +904,19 @@ static struct io_plan *act_one_responder2(struct io_conn *conn,
* * The responder performs an ECDH between its static private key and
* the initiator's ephemeral public key.
*/
if (!hsm_do_ecdh(&h->ss, &h->re))
h->ss = hsm_do_ecdh(h, &h->re);
if (!h->ss)
return handshake_failed(conn, h);

SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, &h->ss, sizeof(h->ss)));
SUPERVERBOSE("# ss=0x%s", tal_hexstr(tmpctx, h->ss, sizeof(*h->ss)));

/* BOLT #8:
*
* 6. `ck, temp_k1 = HKDF(ck, ss)`
* * A new temporary encryption key is generated, which will
* shortly be used to check the authenticating MAC.
*/
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, &h->ss, sizeof(h->ss));
hkdf_two_keys(&h->ck, &h->temp_k, &h->ck, h->ss, sizeof(*h->ss));
SUPERVERBOSE("# ck,temp_k1=0x%s,0x%s",
tal_hexstr(tmpctx, &h->ck, sizeof(h->ck)),
tal_hexstr(tmpctx, &h->temp_k, sizeof(h->temp_k)));
Expand Down
3 changes: 2 additions & 1 deletion connectd/handshake.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef LIGHTNING_CONNECTD_HANDSHAKE_H
#define LIGHTNING_CONNECTD_HANDSHAKE_H
#include "config.h"
#include <ccan/tal/tal.h>
#include <ccan/typesafe_cb/typesafe_cb.h>

struct crypto_state;
Expand Down Expand Up @@ -52,5 +53,5 @@ struct io_plan *responder_handshake_(struct io_conn *conn,
void *cbarg);

/* helper which is defined in connect.c */
bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point);
struct secret *hsm_do_ecdh(const tal_t *ctx, const struct pubkey *point);
#endif /* LIGHTNING_CONNECTD_HANDSHAKE_H */
9 changes: 6 additions & 3 deletions connectd/test/run-initiator-success.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,10 +190,13 @@ static struct io_plan *success(struct io_conn *conn UNUSED,
exit(0);
}

bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point)
struct secret *hsm_do_ecdh(const tal_t *ctx, const struct pubkey *point)
{
return secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
ls_priv.secret.data) == 1;
struct secret *ss = tal(ctx, struct secret);
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
ls_priv.secret.data) != 1)
return tal_free(ss);
return ss;
}

int main(void)
Expand Down
9 changes: 6 additions & 3 deletions connectd/test/run-responder-success.c
Original file line number Diff line number Diff line change
Expand Up @@ -187,10 +187,13 @@ static struct io_plan *success(struct io_conn *conn UNUSED,
exit(0);
}

bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point)
struct secret *hsm_do_ecdh(const tal_t *ctx, const struct pubkey *point)
{
return secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
ls_priv.secret.data) == 1;
struct secret *ss = tal(ctx, struct secret);
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
ls_priv.secret.data) != 1)
return tal_free(ss);
return ss;
}

int main(void)
Expand Down
7 changes: 4 additions & 3 deletions devtools/gossipwith.c
Original file line number Diff line number Diff line change
Expand Up @@ -72,12 +72,13 @@ void peer_failed_connection_lost(void)
exit(0);
}

bool hsm_do_ecdh(struct secret *ss, const struct pubkey *point)
struct secret *hsm_do_ecdh(const tal_t *ctx, const struct pubkey *point)
{
struct secret *ss = tal(ctx, struct secret);
if (secp256k1_ecdh(secp256k1_ctx, ss->data, &point->pubkey,
notsosecret.data) != 1)
errx(1, "ECDH failed");
return true;
return tal_free(ss);
return ss;
}

/* We don't want to discard *any* messages. */
Expand Down
4 changes: 2 additions & 2 deletions hsmd/hsmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -554,8 +554,8 @@ static struct io_plan *handle_ecdh(struct io_conn *conn,
if (!fromwire_hsm_ecdh_req(msg_in, &point))
return bad_req(conn, c, msg_in);

/*~ We simply use the secp256k1_ecdh function, which really shouldn't
* fail (iff the point is invalid). */
/*~ We simply use the secp256k1_ecdh function: if ss.data is invalid,
* we kill them for bad randomness (~1 in 2^127 if ss.data is random) */
node_key(&privkey, NULL);
if (secp256k1_ecdh(secp256k1_ctx, ss.data, &point.pubkey,
privkey.secret.data) != 1) {
Expand Down

0 comments on commit 66de6b8

Please sign in to comment.