From b7bbccd6fa3e59341ca08c1b8bc691db480a2da1 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Thu, 14 Nov 2019 10:44:53 +1030 Subject: [PATCH] common/sphinx: handle decoding of TLV payload. We add routines to decode the expected fields from both legacy and tlv hop formats. Signed-off-by: Rusty Russell --- common/sphinx.c | 102 ++++++++++++++++++++++++++++++++++++++- common/sphinx.h | 15 ++++++ common/test/Makefile | 3 ++ common/test/run-sphinx.c | 39 +++++++++++++++ wallet/test/run-wallet.c | 11 +++++ 5 files changed, 168 insertions(+), 2 deletions(-) diff --git a/common/sphinx.c b/common/sphinx.c index bdf93766b..10a599232 100644 --- a/common/sphinx.c +++ b/common/sphinx.c @@ -477,16 +477,31 @@ static void sphinx_parse_payload(struct route_step *step, const u8 *src) } #endif - /* Legacy hop_data support */ + /* BOLT #4: + * + * The `length` field determines both the length and the format of the + * `hop_payload` field; the following formats are defined: + * + * - Legacy `hop_data` format, identified by a single `0x00` byte for + * length. In this case the `hop_payload_length` is defined to be 32 + * bytes. + * + * - `tlv_payload` format, identified by any length over `1`. In this + * case the `hop_payload_length` is equal to the numeric value of + * `length`. + */ if (src[0] == 0x00) { vsize = 1; raw_size = 32; hop_size = FRAME_SIZE; step->type = SPHINX_V0_PAYLOAD; - } else { + } else if (src[0] > 1) { vsize = bigsize_get(src, 3, &raw_size); hop_size = raw_size + vsize + HMAC_SIZE; step->type = SPHINX_TLV_PAYLOAD; + } else { + step->type = SPHINX_INVALID_PAYLOAD; + return; } /* Copy common pieces over */ @@ -497,6 +512,18 @@ static void sphinx_parse_payload(struct route_step *step, const u8 *src) * later. */ if (step->type == SPHINX_V0_PAYLOAD) deserialize_hop_data(&step->payload.v0, src); + else if (step->type == SPHINX_TLV_PAYLOAD) { + const u8 *tlv = step->raw_payload; + size_t max = tal_bytelen(tlv); + step->payload.tlv = tlv_tlv_payload_new(step); + if (!fromwire_tlvs(&tlv, &max, tlvs_tlv_payload, + TLVS_TLV_PAYLOAD_ARRAY_SIZE, + step->payload.tlv)) { + /* FIXME: record offset of violation for error! */ + step->type = SPHINX_INVALID_PAYLOAD; + return; + } + } } struct onionpacket *create_onionpacket( @@ -781,3 +808,74 @@ struct onionreply *unwrap_onionreply(const tal_t *ctx, return oreply; } + +/** + * Helper to extract fields from ONION_END. + */ +bool route_step_decode_end(const struct route_step *rs, + struct amount_msat *amt_forward, + u32 *outgoing_cltv) +{ + assert(rs->nextcase == ONION_END); + + switch (rs->type) { + case SPHINX_V0_PAYLOAD: + *amt_forward = rs->payload.v0.amt_forward; + *outgoing_cltv = rs->payload.v0.outgoing_cltv; + return true; + case SPHINX_TLV_PAYLOAD: + if (!rs->payload.tlv->amt_to_forward) + return false; + amt_forward->millisatoshis /* Raw: tu64 -> millisatoshis */ + = rs->payload.tlv->amt_to_forward->amt_to_forward; + if (!rs->payload.tlv->outgoing_cltv_value) + return false; + *outgoing_cltv = rs->payload.tlv->outgoing_cltv_value->outgoing_cltv_value; + return true; + case SPHINX_INVALID_PAYLOAD: + return false; + + /* This should probably be removed, as it's just for testing */ + case SPHINX_RAW_PAYLOAD: + abort(); + } + abort(); +} + +/** + * Helper to extract fields from ONION_FORWARD. + */ +bool route_step_decode_forward(const struct route_step *rs, + struct amount_msat *amt_forward, + u32 *outgoing_cltv, + struct short_channel_id *scid) +{ + assert(rs->nextcase == ONION_FORWARD); + + switch (rs->type) { + case SPHINX_V0_PAYLOAD: + *amt_forward = rs->payload.v0.amt_forward; + *outgoing_cltv = rs->payload.v0.outgoing_cltv; + *scid = rs->payload.v0.channel_id; + return true; + case SPHINX_TLV_PAYLOAD: + if (!rs->payload.tlv->amt_to_forward) + return false; + amt_forward->millisatoshis /* Raw: tu64 -> millisatoshis */ + = rs->payload.tlv->amt_to_forward->amt_to_forward; + if (!rs->payload.tlv->outgoing_cltv_value) + return false; + *outgoing_cltv = rs->payload.tlv->outgoing_cltv_value->outgoing_cltv_value; + if (!rs->payload.tlv->short_channel_id) + return false; + *scid = rs->payload.tlv->short_channel_id->short_channel_id; + return true; + case SPHINX_INVALID_PAYLOAD: + return false; + + /* This should probably be removed, as it's just for testing */ + case SPHINX_RAW_PAYLOAD: + abort(); + } + abort(); +} diff --git a/common/sphinx.h b/common/sphinx.h index 6e39f5aef..790aa3a2c 100644 --- a/common/sphinx.h +++ b/common/sphinx.h @@ -81,6 +81,7 @@ struct route_step { enum sphinx_payload_type type; union { struct hop_data_legacy v0; + struct tlv_tlv_payload *tlv; } payload; u8 *raw_payload; }; @@ -230,4 +231,18 @@ void sphinx_add_v0_hop(struct sphinx_path *path, const struct pubkey *pubkey, void sphinx_add_raw_hop(struct sphinx_path *path, const struct pubkey *pubkey, enum sphinx_payload_type type, const u8 *payload); +/** + * Helper to extract fields from ONION_END. + */ +bool route_step_decode_end(const struct route_step *rs, + struct amount_msat *amt_forward, + u32 *outgoing_cltv); + +/** + * Helper to extract fields from ONION_FORWARD. + */ +bool route_step_decode_forward(const struct route_step *rs, + struct amount_msat *amt_forward, + u32 *outgoing_cltv, + struct short_channel_id *scid); #endif /* LIGHTNING_COMMON_SPHINX_H */ diff --git a/common/test/Makefile b/common/test/Makefile index 59e2a6415..3974d2099 100644 --- a/common/test/Makefile +++ b/common/test/Makefile @@ -11,6 +11,9 @@ $(COMMON_TEST_OBJS): $(COMMON_HEADERS) $(WIRE_HEADERS) $(COMMON_SRC) ALL_TEST_PROGRAMS += $(COMMON_TEST_PROGRAMS) ALL_OBJS += $(COMMON_TEST_PROGRAMS:=.o) +# Sphinx test wants to decode TLVs. +common/test/run-sphinx: wire/gen_onion_wire.o + update-mocks: $(COMMON_TEST_SRC:%=update-mocks/%) check-units: $(COMMON_TEST_PROGRAMS:%=unittest/%) diff --git a/common/test/run-sphinx.c b/common/test/run-sphinx.c index b79d24919..b6e5c4ede 100644 --- a/common/test/run-sphinx.c +++ b/common/test/run-sphinx.c @@ -43,13 +43,31 @@ const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy /* Generated stub for fromwire_amount_msat */ struct amount_msat fromwire_amount_msat(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_amount_msat called!\n"); abort(); } +/* Generated stub for fromwire_bigsize */ +bigsize_t fromwire_bigsize(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bigsize called!\n"); abort(); } /* Generated stub for fromwire_fail */ const void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } /* Generated stub for fromwire_short_channel_id */ void fromwire_short_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "fromwire_short_channel_id called!\n"); abort(); } +/* Generated stub for fromwire_tlvs */ +bool fromwire_tlvs(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + const struct tlv_record_type types[] UNNEEDED, + size_t num_types UNNEEDED, + void *record UNNEEDED) +{ fprintf(stderr, "fromwire_tlvs called!\n"); abort(); } +/* Generated stub for fromwire_tu32 */ +u32 fromwire_tu32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu32 called!\n"); abort(); } +/* Generated stub for fromwire_tu64 */ +u64 fromwire_tu64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_tu64 called!\n"); abort(); } /* Generated stub for fromwire_u16 */ u16 fromwire_u16(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u16 called!\n"); abort(); } @@ -59,16 +77,34 @@ u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) /* Generated stub for fromwire_u8 */ u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) { fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } /* Generated stub for towire */ void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) { fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_amount_msat */ +void towire_amount_msat(u8 **pptr UNNEEDED, const struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "towire_amount_msat called!\n"); abort(); } +/* Generated stub for towire_bigsize */ +void towire_bigsize(u8 **pptr UNNEEDED, const bigsize_t val UNNEEDED) +{ fprintf(stderr, "towire_bigsize called!\n"); abort(); } /* Generated stub for towire_pad */ void towire_pad(u8 **pptr UNNEEDED, size_t num UNNEEDED) { fprintf(stderr, "towire_pad called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } /* Generated stub for towire_short_channel_id */ void towire_short_channel_id(u8 **pptr UNNEEDED, const struct short_channel_id *short_channel_id UNNEEDED) { fprintf(stderr, "towire_short_channel_id called!\n"); abort(); } +/* Generated stub for towire_tu32 */ +void towire_tu32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_tu32 called!\n"); abort(); } +/* Generated stub for towire_tu64 */ +void towire_tu64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_tu64 called!\n"); abort(); } /* Generated stub for towire_u16 */ void towire_u16(u8 **pptr UNNEEDED, u16 v UNNEEDED) { fprintf(stderr, "towire_u16 called!\n"); abort(); } @@ -78,6 +114,9 @@ void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) /* Generated stub for towire_u64 */ void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) { fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } /* AUTOGENERATED MOCKS END */ secp256k1_context *secp256k1_ctx; diff --git a/wallet/test/run-wallet.c b/wallet/test/run-wallet.c index f11d87792..6a66f6860 100644 --- a/wallet/test/run-wallet.c +++ b/wallet/test/run-wallet.c @@ -535,6 +535,17 @@ struct route_step *process_onionpacket( const size_t assocdatalen ) { fprintf(stderr, "process_onionpacket called!\n"); abort(); } +/* Generated stub for route_step_decode_end */ +bool route_step_decode_end(const struct route_step *rs UNNEEDED, + struct amount_msat *amt_forward UNNEEDED, + u32 *outgoing_cltv UNNEEDED) +{ fprintf(stderr, "route_step_decode_end called!\n"); abort(); } +/* Generated stub for route_step_decode_forward */ +bool route_step_decode_forward(const struct route_step *rs UNNEEDED, + struct amount_msat *amt_forward UNNEEDED, + u32 *outgoing_cltv UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "route_step_decode_forward called!\n"); abort(); } /* Generated stub for serialize_onionpacket */ u8 *serialize_onionpacket( const tal_t *ctx UNNEEDED,