From cae283087d4bbfe0da9e6d485408c9b2cdb8fbad Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Fri, 13 Jan 2017 22:50:10 +0100 Subject: [PATCH] sphinx: Committing the onion packet to the payment-hash The sphinx onion packet now commits to the HTLC payment-hash it is associated with. This prevents replay attacks with the same onion. --- daemon/pay.c | 3 ++- daemon/peer.c | 6 ++++-- daemon/sphinx.c | 26 ++++++++++++++++++-------- daemon/sphinx.h | 12 ++++++++++-- test/test_sphinx.c | 9 +++++++-- 5 files changed, 41 insertions(+), 15 deletions(-) diff --git a/daemon/pay.c b/daemon/pay.c index d69697f28..1a3c4a329 100644 --- a/daemon/pay.c +++ b/daemon/pay.c @@ -442,7 +442,8 @@ static void json_sendpay(struct command *cmd, randombytes_buf(&sessionkey, sizeof(sessionkey)); /* Onion will carry us from first peer onwards. */ - packet = create_onionpacket(cmd, ids, hoppayloads, sessionkey); + packet = create_onionpacket(cmd, ids, hoppayloads, sessionkey, + rhash.u.u8, sizeof(struct sha256)); onion = serialize_onionpacket(cmd, packet); if (pc) diff --git a/daemon/peer.c b/daemon/peer.c index 96fb3a841..dd631e19e 100644 --- a/daemon/peer.c +++ b/daemon/peer.c @@ -887,7 +887,8 @@ static void their_htlc_added(struct peer *peer, struct htlc *htlc, packet = parse_onionpacket(peer, htlc->routing, tal_count(htlc->routing)); if (packet) - step = process_onionpacket(packet, packet, &pk); + step = process_onionpacket(packet, packet, &pk, htlc->rhash.u.u8, + sizeof(htlc->rhash)); if (!step) { log_unusual(peer->log, "Bad onion, failing HTLC %"PRIu64, @@ -4730,7 +4731,8 @@ static void json_newhtlc(struct command *cmd, hoppayloads = tal_arrz(cmd, struct hoppayload, 1); memcpy(&path[0], peer->id, sizeof(struct pubkey)); randombytes_buf(&sessionkey, sizeof(sessionkey)); - packet = create_onionpacket(cmd, path, hoppayloads, sessionkey); + packet = create_onionpacket(cmd, path, hoppayloads, sessionkey, + rhash.u.u8, sizeof(rhash)); onion = serialize_onionpacket(cmd, packet); log_debug(peer->log, "JSON command to add new HTLC"); diff --git a/daemon/sphinx.c b/daemon/sphinx.c index db64e59c3..216429702 100644 --- a/daemon/sphinx.c +++ b/daemon/sphinx.c @@ -159,13 +159,18 @@ static bool compute_hmac( return true; } -static void compute_packet_hmac(const struct onionpacket *packet, u8 *mukey, u8 *hmac) +static void compute_packet_hmac(const struct onionpacket *packet, + const u8 *assocdata, const size_t assocdatalen, + u8 *mukey, u8 *hmac) { - u8 mactemp[ROUTING_INFO_SIZE + TOTAL_HOP_PAYLOAD_SIZE]; + u8 mactemp[ROUTING_INFO_SIZE + TOTAL_HOP_PAYLOAD_SIZE + assocdatalen]; u8 mac[32]; + int pos = 0; + + write_buffer(mactemp, packet->routinginfo, ROUTING_INFO_SIZE, &pos); + write_buffer(mactemp, packet->hoppayloads, TOTAL_HOP_PAYLOAD_SIZE, &pos); + write_buffer(mactemp, assocdata, assocdatalen, &pos); - memcpy(mactemp, packet->routinginfo, ROUTING_INFO_SIZE); - memcpy(mactemp + ROUTING_INFO_SIZE, packet->hoppayloads, TOTAL_HOP_PAYLOAD_SIZE); compute_hmac(mac, mactemp, sizeof(mactemp), mukey, KEY_LEN); memcpy(hmac, mac, 20); } @@ -340,7 +345,9 @@ struct onionpacket *create_onionpacket( const tal_t *ctx, struct pubkey *path, struct hoppayload hoppayloads[], - const u8 *sessionkey + const u8 *sessionkey, + const u8 *assocdata, + const size_t assocdatalen ) { struct onionpacket *packet = talz(ctx, struct onionpacket); @@ -394,7 +401,8 @@ struct onionpacket *create_onionpacket( memcpy(packet->hoppayloads + len, hopfiller, sizeof(hopfiller)); } - compute_packet_hmac(packet, keys.mu, nexthmac); + compute_packet_hmac(packet, assocdata, assocdatalen, keys.mu, + nexthmac); pubkey_hash160(nextaddr, &path[i]); } memcpy(packet->mac, nexthmac, sizeof(nexthmac)); @@ -409,7 +417,9 @@ struct onionpacket *create_onionpacket( struct route_step *process_onionpacket( const tal_t *ctx, const struct onionpacket *msg, - struct privkey *hop_privkey + struct privkey *hop_privkey, + const u8 *assocdata, + const size_t assocdatalen ) { struct route_step *step = talz(ctx, struct route_step); @@ -427,7 +437,7 @@ struct route_step *process_onionpacket( create_shared_secret(secret, &msg->ephemeralkey, hop_privkey->secret); generate_key_set(secret, &keys); - compute_packet_hmac(msg, keys.mu, hmac); + compute_packet_hmac(msg, assocdata, assocdatalen, keys.mu, hmac); if (memcmp(msg->mac, hmac, sizeof(hmac)) != 0) { warnx("Computed MAC does not match expected MAC, the message was modified."); diff --git a/daemon/sphinx.h b/daemon/sphinx.h index cf0459919..65813f3bf 100644 --- a/daemon/sphinx.h +++ b/daemon/sphinx.h @@ -57,12 +57,16 @@ struct route_step { * HOP_PAYLOAD_SIZE bytes) * @num_hops: path length in nodes * @sessionkey: 20 byte random session key to derive secrets from + * @assocdata: associated data to commit to in HMACs + * @assocdatalen: length of the assocdata */ struct onionpacket *create_onionpacket( const tal_t * ctx, struct pubkey path[], struct hoppayload hoppayloads[], - const u8 * sessionkey + const u8 * sessionkey, + const u8 *assocdata, + const size_t assocdatalen ); /** @@ -73,11 +77,15 @@ struct onionpacket *create_onionpacket( * @packet: incoming packet being processed * @hop_privkey: the processing node's private key to decrypt the packet * @hoppayload: the per-hop payload destined for the processing node. + * @assocdata: associated data to commit to in HMACs + * @assocdatalen: length of the assocdata */ struct route_step *process_onionpacket( const tal_t * ctx, const struct onionpacket *packet, - struct privkey *hop_privkey + struct privkey *hop_privkey, + const u8 *assocdata, + const size_t assocdatalen ); /** diff --git a/test/test_sphinx.c b/test/test_sphinx.c index 73fe693ed..8ae231647 100644 --- a/test/test_sphinx.c +++ b/test/test_sphinx.c @@ -16,6 +16,8 @@ int main(int argc, char **argv) { bool generate = false, decode = false; const tal_t *ctx = talz(NULL, tal_t); + u8 assocdata[32]; + memset(assocdata, 'B', sizeof(assocdata)); secp256k1_ctx = secp256k1_context_create( SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN); @@ -56,7 +58,9 @@ int main(int argc, char **argv) struct onionpacket *res = create_onionpacket(ctx, path, hoppayloads, - sessionkey); + sessionkey, + assocdata, + sizeof(assocdata)); u8 *serialized = serialize_onionpacket(ctx, res); if (!serialized) @@ -87,7 +91,8 @@ int main(int argc, char **argv) if (!msg) errx(1, "Error parsing message."); - step = process_onionpacket(ctx, msg, &seckey); + step = process_onionpacket(ctx, msg, &seckey, assocdata, + sizeof(assocdata)); if (!step->next) errx(1, "Error processing message.");