Skip to content

Commit b44e6e8

Browse files
committed
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>
1 parent 53cb014 commit b44e6e8

File tree

2 files changed

+175
-13
lines changed

2 files changed

+175
-13
lines changed

channeld/channeld.c

Lines changed: 156 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include <hsmd/gen_hsm_wire.h>
5656
#include <inttypes.h>
5757
#include <secp256k1.h>
58+
#include <sodium/crypto_aead_chacha20poly1305.h>
5859
#include <stdio.h>
5960
#include <wire/gen_common_wire.h>
6061
#include <wire/gen_onion_wire.h>
@@ -1624,12 +1625,44 @@ static bool channeld_handle_custommsg(const u8 *msg)
16241625
}
16251626

16261627
#if EXPERIMENTAL_FEATURES
1628+
/* H(E(i) || ss(i)) */
1629+
static struct sha256 hash_e_and_ss(const struct pubkey *e,
1630+
const struct secret *ss)
1631+
{
1632+
u8 der[PUBKEY_CMPR_LEN];
1633+
struct sha256_ctx shactx;
1634+
struct sha256 h;
1635+
1636+
pubkey_to_der(der, e);
1637+
sha256_init(&shactx);
1638+
sha256_update(&shactx, der, sizeof(der));
1639+
sha256_update(&shactx, ss->data, sizeof(ss->data));
1640+
sha256_done(&shactx, &h);
1641+
1642+
return h;
1643+
}
1644+
1645+
/* E(i-1) = H(E(i) || ss(i)) * E(i) */
1646+
static struct pubkey next_pubkey(const struct pubkey *pk,
1647+
const struct sha256 *h)
1648+
{
1649+
struct pubkey ret;
1650+
1651+
ret = *pk;
1652+
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx, &ret.pubkey, h->u.u8)
1653+
!= 1)
1654+
abort();
1655+
1656+
return ret;
1657+
}
1658+
16271659
/* Peer sends onion msg. */
16281660
static void handle_onion_message(struct peer *peer, const u8 *msg)
16291661
{
16301662
enum onion_type badreason;
16311663
struct onionpacket op;
1632-
struct secret ss;
1664+
struct secret ss, *blinding_ss;
1665+
struct pubkey *blinding_in;
16331666
struct route_step *rs;
16341667
u8 onion[TOTAL_PACKET_SIZE];
16351668
const u8 *cursor;
@@ -1644,11 +1677,6 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
16441677
&peer->channel_id,
16451678
"Bad onion_message %s", tal_hex(peer, msg));
16461679

1647-
if (tlvs->blinding) {
1648-
status_broken("FIXME: Handle blinding!");
1649-
return;
1650-
}
1651-
16521680
/* We unwrap the onion now. */
16531681
badreason = parse_onionpacket(onion, TOTAL_PACKET_SIZE, &op);
16541682
if (badreason != 0) {
@@ -1657,7 +1685,38 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
16571685
return;
16581686
}
16591687

1660-
/* Because wire takes struct pubkey. */
1688+
if (tlvs->blinding) {
1689+
struct secret hmac;
1690+
1691+
/* E(i) */
1692+
blinding_in = tal(msg, struct pubkey);
1693+
*blinding_in = tlvs->blinding->blinding;
1694+
status_debug("blinding in = %s",
1695+
type_to_string(tmpctx, struct pubkey, blinding_in));
1696+
blinding_ss = tal(msg, struct secret);
1697+
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, blinding_in));
1698+
1699+
if (!fromwire_hsm_ecdh_resp(msg, blinding_ss))
1700+
status_failed(STATUS_FAIL_HSM_IO,
1701+
"Reading ecdh response for blinding");
1702+
1703+
/* b(i) = HMAC256("blinded_node_id", ss(i)) * k(i) */
1704+
subkey_from_hmac("blinded_node_id", blinding_ss, &hmac);
1705+
1706+
/* We instead tweak the *ephemeral* key from the onion and use
1707+
* our normal privkey: since hsmd knows only how to ECDH with
1708+
* our real key */
1709+
if (secp256k1_ec_pubkey_tweak_mul(secp256k1_ctx,
1710+
&op.ephemeralkey.pubkey,
1711+
hmac.data) != 1) {
1712+
status_debug("onion msg: can't tweak pubkey");
1713+
return;
1714+
}
1715+
} else {
1716+
blinding_ss = NULL;
1717+
blinding_in = NULL;
1718+
}
1719+
16611720
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, &op.ephemeralkey));
16621721
if (!fromwire_hsm_ecdh_resp(msg, &ss))
16631722
status_failed(STATUS_FAIL_HSM_IO, "Reading ecdh response");
@@ -1693,6 +1752,72 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
16931752
return;
16941753
}
16951754

1755+
/* If we weren't given a blinding factor, tlv can provide one. */
1756+
if (om->blinding && !blinding_ss) {
1757+
/* E(i) */
1758+
blinding_in = tal(msg, struct pubkey);
1759+
*blinding_in = om->blinding->blinding;
1760+
blinding_ss = tal(msg, struct secret);
1761+
1762+
msg = hsm_req(tmpctx, towire_hsm_ecdh_req(tmpctx, blinding_in));
1763+
1764+
if (!fromwire_hsm_ecdh_resp(msg, blinding_ss))
1765+
status_failed(STATUS_FAIL_HSM_IO,
1766+
"Reading ecdh response for om blinding");
1767+
}
1768+
1769+
if (om->enctlv) {
1770+
const unsigned char npub[crypto_aead_chacha20poly1305_ietf_NPUBBYTES] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
1771+
u8 *dec;
1772+
struct secret rho;
1773+
int ret;
1774+
1775+
if (!blinding_ss) {
1776+
status_debug("enctlv but no blinding?");
1777+
return;
1778+
}
1779+
1780+
/* We need this to decrypt enctlv */
1781+
subkey_from_hmac("rho", blinding_ss, &rho);
1782+
1783+
/* Overrides next_scid / next_node */
1784+
if (tal_bytelen(om->enctlv->enctlv)
1785+
< crypto_aead_chacha20poly1305_ietf_ABYTES) {
1786+
status_debug("enctlv too short for mac");
1787+
return;
1788+
}
1789+
1790+
dec = tal_arr(msg, u8,
1791+
tal_bytelen(om->enctlv->enctlv)
1792+
- crypto_aead_chacha20poly1305_ietf_ABYTES);
1793+
ret = crypto_aead_chacha20poly1305_ietf_decrypt(dec, NULL,
1794+
NULL,
1795+
om->enctlv->enctlv,
1796+
tal_bytelen(om->enctlv->enctlv),
1797+
NULL, 0,
1798+
npub,
1799+
rho.data);
1800+
if (ret != 0)
1801+
errx(1, "Failed to decrypt enctlv field");
1802+
1803+
status_debug("enctlv -> %s", tal_hex(tmpctx, dec));
1804+
1805+
/* Replace onionmsg with one from enctlv */
1806+
cursor = dec;
1807+
maxlen = tal_bytelen(dec);
1808+
1809+
om = tlv_onionmsg_payload_new(msg);
1810+
if (!fromwire_onionmsg_payload(&cursor, &maxlen, om)) {
1811+
status_debug("onion msg: invalid enctlv onionmsg_payload %s",
1812+
tal_hex(tmpctx, dec));
1813+
return;
1814+
}
1815+
} else if (blinding_ss && rs->nextcase != ONION_END) {
1816+
status_debug("Onion had %s, but not enctlv?",
1817+
tlvs->blinding ? "blinding" : "om blinding");
1818+
return;
1819+
}
1820+
16961821
if (om->next_short_channel_id)
16971822
next_scid = &om->next_short_channel_id->short_channel_id;
16981823
else
@@ -1710,25 +1835,44 @@ static void handle_onion_message(struct peer *peer, const u8 *msg)
17101835
}
17111836

17121837
if (rs->nextcase == ONION_END) {
1713-
/* FIXME: Handle reply_path */
1714-
/* payload may not be valid, so we hand it pre-decrypted to lightningd */
1838+
struct pubkey *blinding;
1839+
const struct onionmsg_path **path;
1840+
1841+
if (om->reply_path) {
1842+
blinding = &om->reply_path->blinding;
1843+
path = cast_const2(const struct onionmsg_path **,
1844+
om->reply_path->path);
1845+
} else {
1846+
blinding = NULL;
1847+
path = NULL;
1848+
}
17151849
wire_sync_write(MASTER_FD,
17161850
take(towire_got_onionmsg_to_us(NULL,
1717-
NULL,
1718-
NULL)));
1851+
blinding,
1852+
path)));
17191853
} else {
1854+
struct pubkey *next_blinding;
1855+
17201856
/* This *MUST* have instructions on where to go next. */
17211857
if (!next_scid && !next_node) {
17221858
status_debug("onion msg: no next field in %s",
17231859
tal_hex(tmpctx, rs->raw_payload));
17241860
return;
17251861
}
17261862

1863+
if (blinding_ss) {
1864+
/* E(i-1) = H(E(i) || ss(i)) * E(i) */
1865+
struct sha256 h = hash_e_and_ss(blinding_in, blinding_ss);
1866+
next_blinding = tal(msg, struct pubkey);
1867+
*next_blinding = next_pubkey(blinding_in, &h);
1868+
} else
1869+
next_blinding = NULL;
1870+
17271871
wire_sync_write(MASTER_FD,
17281872
take(towire_got_onionmsg_forward(NULL,
17291873
next_scid,
17301874
next_node,
1731-
NULL,
1875+
next_blinding,
17321876
serialize_onionpacket(tmpctx, rs->next))));
17331877
}
17341878
}

tests/test_misc.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2163,6 +2163,8 @@ def test_sendcustommsg(node_factory):
21632163
def test_sendonionmessage(node_factory):
21642164
l1, l2, l3 = node_factory.line_graph(3)
21652165

2166+
blindedpathtool = os.path.join(os.path.dirname(__file__), "..", "devtools", "blindedpath")
2167+
21662168
l1.rpc.call('sendonionmessage',
21672169
{'hops':
21682170
[{'id': l2.info['id']},
@@ -2177,7 +2179,23 @@ def test_sendonionmessage(node_factory):
21772179
{'id': l3.info['id']}]})
21782180
assert l3.daemon.wait_for_log('Got onionmsg')
21792181

2180-
# FIXME: Test blinded path!
2182+
# Now test blinded path.
2183+
output = subprocess.check_output(
2184+
[blindedpathtool, '--simple-output', 'create', l2.info['id'], l3.info['id']]
2185+
).decode('ASCII').strip()
2186+
2187+
# First line is blinding, then <peerid> then <encblob>.
2188+
blinding, p1, p1enc, p2 = output.split('\n')
2189+
# First hop can't be blinded!
2190+
assert p1 == l2.info['id']
2191+
2192+
l1.rpc.call('sendonionmessage',
2193+
{'hops':
2194+
[{'id': l2.info['id'],
2195+
'blinding': blinding,
2196+
'enctlv': p1enc},
2197+
{'id': p2}]})
2198+
assert l3.daemon.wait_for_log('Got onionmsg')
21812199

21822200

21832201
@unittest.skipIf(not DEVELOPER, "needs --dev-force-privkey")

0 commit comments

Comments
 (0)