Browse Source

channeld: handle encblob and blinding in messages.

This is based on https://github.com/lightningnetwork/lightning-rfc/blob/route-blinding/proposals/route-blinding.md

Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
travis-debug
Rusty Russell 5 years ago
parent
commit
8b8cbb9397
  1. 168
      channeld/channeld.c
  2. 20
      tests/test_misc.py

168
channeld/channeld.c

@ -55,6 +55,7 @@
#include <hsmd/gen_hsm_wire.h>
#include <inttypes.h>
#include <secp256k1.h>
#include <sodium/crypto_aead_chacha20poly1305.h>
#include <stdio.h>
#include <wire/gen_common_wire.h>
#include <wire/gen_onion_wire.h>
@ -1623,12 +1624,44 @@ static bool channeld_handle_custommsg(const u8 *msg)
}
#if EXPERIMENTAL_FEATURES
/* H(E(i) || ss(i)) */
static struct sha256 hash_e_and_ss(const struct pubkey *e,
const struct secret *ss)
{
u8 der[PUBKEY_CMPR_LEN];
struct sha256_ctx shactx;
struct sha256 h;
pubkey_to_der(der, e);
sha256_init(&shactx);
sha256_update(&shactx, der, sizeof(der));
sha256_update(&shactx, ss->data, sizeof(ss->data));
sha256_done(&shactx, &h);
return h;
}
/* E(i-1) = H(E(i) || ss(i)) * E(i) */
static struct pubkey next_pubkey(const struct pubkey *pk,
const struct sha256 *h)
{
struct pubkey ret;
ret = *pk;
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &ret.pubkey, h->u.u8)
!= 1)
abort();
return ret;
}
/* Peer sends onion msg. */
static void handle_onion_message(struct peer *peer, const u8 *msg)
{
enum onion_type badreason;
struct onionpacket op;
struct secret ss;
struct secret ss, *blinding_ss;
struct pubkey *blinding_in;
struct route_step *rs;
u8 onion[TOTAL_PACKET_SIZE];
const u8 *cursor;
@ -1643,11 +1676,6 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
&peer->channel_id,
"Bad onion_message %s", tal_hex(peer, msg));
if (tlvs->blinding) {
status_broken("FIXME: Handle blinding!");
return;
}
/* We unwrap the onion now. */
badreason = parse_onionpacket(onion, TOTAL_PACKET_SIZE, &op);
if (badreason != 0) {
@ -1656,7 +1684,38 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
return;
}
/* Because wire takes struct pubkey. */
if (tlvs->blinding) {
struct secret hmac;
/* E(i) */
blinding_in = tal(msg, struct pubkey);
*blinding_in = tlvs->blinding->blinding;
status_debug("blinding in = %s",
type_to_string(tmpctx, struct pubkey, blinding_in));
blinding_ss = tal(msg, struct secret);
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, blinding_in));
if (!fromwire_hsm_ecdh_resp(msg, blinding_ss))
status_failed(STATUS_FAIL_HSM_IO,
"Reading ecdh response for blinding");
/* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */
subkey_from_hmac("blinded_node_id", blinding_ss, &hmac);
/* We instead tweak the *ephemeral* key from the onion and use
* our normal privkey: since hsmd knows only how to ECDH with
* our real key */
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
&op.ephemeralkey.pubkey,
hmac.data) != 1) {
status_debug("onion msg: can't tweak pubkey");
return;
}
} else {
blinding_ss = NULL;
blinding_in = NULL;
}
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, &op.ephemeralkey));
if (!fromwire_hsm_ecdh_resp(msg, &ss))
status_failed(STATUS_FAIL_HSM_IO, "Reading ecdh response");
@ -1692,6 +1751,72 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
return;
}
/* If we weren't given a blinding factor, tlv can provide one. */
if (om->blinding && !blinding_ss) {
/* E(i) */
blinding_in = tal(msg, struct pubkey);
*blinding_in = om->blinding->blinding;
blinding_ss = tal(msg, struct secret);
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, blinding_in));
if (!fromwire_hsm_ecdh_resp(msg, blinding_ss))
status_failed(STATUS_FAIL_HSM_IO,
"Reading ecdh response for om blinding");
}
if (om->enctlv) {
const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
u8 *dec;
struct secret rho;
int ret;
if (!blinding_ss) {
status_debug("enctlv but no blinding?");
return;
}
/* We need this to decrypt enctlv */
subkey_from_hmac("rho", blinding_ss, &rho);
/* Overrides next_scid / next_node */
if (tal_bytelen(om->enctlv->enctlv)
< crypto_aead_chacha20poly1305_ietf_ABYTES) {
status_debug("enctlv too short for mac");
return;
}
dec = tal_arr(msg, u8,
tal_bytelen(om->enctlv->enctlv)
- crypto_aead_chacha20poly1305_ietf_ABYTES);
ret = crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL,
NULL,
om->enctlv->enctlv,
tal_bytelen(om->enctlv->enctlv),
NULL, 0,
npub,
rho.data);
if (ret != 0)
errx(1, "Failed to decrypt enctlv field");
status_debug("enctlv -> %s", tal_hex(tmpctx, dec));
/* Replace onionmsg with one from enctlv */
cursor = dec;
maxlen = tal_bytelen(dec);
om = tlv_onionmsg_payload_new(msg);
if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) {
status_debug("onion msg: invalid enctlv onionmsg_payload %s",
tal_hex(tmpctx, dec));
return;
}
} else if (blinding_ss && rs->nextcase != ONION_END) {
status_debug("Onion had %s, but not enctlv?",
tlvs->blinding ? "blinding" : "om blinding");
return;
}
if (om->next_short_channel_id)
next_scid = &om->next_short_channel_id->short_channel_id;
else
@ -1709,13 +1834,24 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
}
if (rs->nextcase == ONION_END) {
/* FIXME: Handle reply_path */
/* payload may not be valid, so we hand it pre-decrypted to lightningd */
struct pubkey *blinding;
const struct onionmsg_path **path;
if (om->reply_path) {
blinding = &om->reply_path->blinding;
path = cast_const2(const struct onionmsg_path **,
om->reply_path->path);
} else {
blinding = NULL;
path = NULL;
}
wire_sync_write(MASTER_FD,
take(towire_got_onionmsg_to_us(NULL,
NULL,
NULL)));
blinding,
path)));
} else {
struct pubkey *next_blinding;
/* This *MUST* have instructions on where to go next. */
if (!next_scid && !next_node) {
status_debug("onion msg: no next field in %s",
@ -1723,11 +1859,19 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
return;
}
if (blinding_ss) {
/* E(i-1) = H(E(i) || ss(i)) * E(i) */
struct sha256 h = hash_e_and_ss(blinding_in, blinding_ss);
next_blinding = tal(msg, struct pubkey);
*next_blinding = next_pubkey(blinding_in, &h);
} else
next_blinding = NULL;
wire_sync_write(MASTER_FD,
take(towire_got_onionmsg_forward(NULL,
next_scid,
next_node,
NULL,
next_blinding,
serialize_onionpacket(tmpctx, rs->next))));
}
}

20
tests/test_misc.py

@ -2182,6 +2182,8 @@ def test_sendcustommsg(node_factory):
def test_sendonionmessage(node_factory):
l1, l2, l3 = node_factory.line_graph(3)
blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath")
l1.rpc.call('sendonionmessage',
{'hops':
[{'id': l2.info['id']},
@ -2196,7 +2198,23 @@ def test_sendonionmessage(node_factory):
{'id': l3.info['id']}]})
assert l3.daemon.wait_for_log('Got onionmsg')
# FIXME: Test blinded path!
# Now test blinded path.
output = subprocess.check_output(
[blindedpathtool, '--simple-output', 'create', l2.info['id'], l3.info['id']]
).decode('ASCII').strip()
# First line is blinding, then <peerid> then <encblob>.
blinding, p1, p1enc, p2 = output.split('\n')
# First hop can't be blinded!
assert p1 == l2.info['id']
l1.rpc.call('sendonionmessage',
{'hops':
[{'id': l2.info['id'],
'blinding': blinding,
'enctlv': p1enc},
{'id': p2}]})
assert l3.daemon.wait_for_log('Got onionmsg')
@unittest.skipIf(not DEVELOPER, "needs --dev-force-privkey")

Loading…
Cancel
Save