Skip to content

Commit 7bf121f

Browse files
committed
Add helpers for serializing and deserializing attr data
Implemented methods to wrap attribution data, update the attribution data. Key Changes: - Use 'ammagext' subkey to crypt the attr data. - Write method to add HMACs to the attr data. - Update attribution data to a given onionreply. - Write method to verify attr data to find erring node. - Use all these helpers inside unwrap_onionreply().
1 parent 9eefe79 commit 7bf121f

10 files changed

+169
-15
lines changed

common/sphinx.c

Lines changed: 134 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -818,14 +818,15 @@ struct onionreply *create_onionreply(const tal_t *ctx,
818818
towire(&reply->contents, payload, tal_count(payload));
819819
tal_free(payload);
820820

821+
reply->attr_data = NULL;
821822
return reply;
822823
}
823824

824825
struct onionreply *wrap_onionreply(const tal_t *ctx,
825826
const struct secret *shared_secret,
826827
const struct onionreply *reply)
827828
{
828-
struct secret key;
829+
struct secret key, attr_key;
829830
struct onionreply *result = tal(ctx, struct onionreply);
830831

831832
/* BOLT #4:
@@ -839,9 +840,109 @@ struct onionreply *wrap_onionreply(const tal_t *ctx,
839840
subkey_from_hmac("ammag", shared_secret, &key);
840841
result->contents = tal_dup_talarr(result, u8, reply->contents);
841842
xor_cipher_stream(result->contents, &key, tal_bytelen(result->contents));
843+
844+
if (reply->attr_data) {
845+
result->attr_data = tal(ctx, struct attribution_data);
846+
subkey_from_hmac("ammagext", shared_secret, &attr_key);
847+
result->attr_data->htlc_hold_time = tal_dup_talarr(result, u8, reply->attr_data->htlc_hold_time);
848+
xor_cipher_stream_off(&attr_key, 0, result->attr_data->htlc_hold_time, tal_bytelen(result->attr_data->htlc_hold_time));
849+
result->attr_data->truncated_hmac = tal_dup_talarr(result, u8, reply->attr_data->truncated_hmac);
850+
xor_cipher_stream_off(&attr_key,
851+
tal_bytelen(result->attr_data->htlc_hold_time),
852+
result->attr_data->truncated_hmac,
853+
tal_bytelen(result->attr_data->truncated_hmac));
854+
} else {
855+
result->attr_data = NULL;
856+
}
857+
842858
return result;
843859
}
844860

861+
static void write_downstream_hmacs(struct crypto_auth_hmacsha256_state *state, int pos, u8 *truncated_hmac) {
862+
int hmac_idx = MAX_HOPS + MAX_HOPS - pos - 1;
863+
for (int i = 0; i < pos; i++) {
864+
hmac_update(state, truncated_hmac + (hmac_idx * TRUNC_HMAC_LEN), TRUNC_HMAC_LEN);
865+
int block_size = MAX_HOPS - i - 1;
866+
hmac_idx += block_size;
867+
}
868+
}
869+
870+
static void add_hmacs_to_attribution_data(struct onionreply *failonion, struct secret *shared_secret) {
871+
struct secret um_key;
872+
subkey_from_hmac("um", shared_secret, &um_key);
873+
for (int i = 0; i < MAX_HOPS; i++) {
874+
struct crypto_auth_hmacsha256_state state;
875+
struct hmac hmac;
876+
int pos = MAX_HOPS - i - 1;
877+
hmac_start(&state, um_key.data, sizeof(um_key.data));
878+
hmac_update(&state, failonion->contents, tal_bytelen(failonion->contents));
879+
hmac_update(&state, failonion->attr_data->htlc_hold_time, (pos + 1) * HOLD_TIME_LEN);
880+
write_downstream_hmacs(&state, pos, failonion->attr_data->truncated_hmac);
881+
hmac_done(&state, &hmac);
882+
memcpy(failonion->attr_data->truncated_hmac + (i * TRUNC_HMAC_LEN), hmac.bytes, TRUNC_HMAC_LEN);
883+
}
884+
885+
}
886+
887+
void update_attributable_data(struct onionreply *failonion, u32 hold_times, struct secret *shared_secret) {
888+
if (!failonion->attr_data) {
889+
failonion->attr_data = tal(failonion, struct attribution_data);
890+
failonion->attr_data->htlc_hold_time = tal_arrz(failonion, u8, 80);
891+
failonion->attr_data->truncated_hmac = tal_arrz(failonion, u8, 840);
892+
} else {
893+
/* Right shift */
894+
memmove(&failonion->attr_data->htlc_hold_time[HOLD_TIME_LEN], failonion->attr_data->htlc_hold_time, HOLD_TIME_LEN * (MAX_HOPS - 1));
895+
int src_index = HMAC_COUNT - 2;
896+
int dest_index = HMAC_COUNT - 1;
897+
int copy_len = 1;
898+
899+
for (int i = 0; i < MAX_HOPS - 1; i++) {
900+
memmove(&failonion->attr_data->truncated_hmac[dest_index * TRUNC_HMAC_LEN],
901+
&failonion->attr_data->truncated_hmac[src_index * TRUNC_HMAC_LEN],
902+
copy_len * TRUNC_HMAC_LEN);
903+
if (i == MAX_HOPS - 2)
904+
break;
905+
906+
copy_len += 1;
907+
src_index -= copy_len + 1;
908+
dest_index -= copy_len;
909+
}
910+
}
911+
u8 *hold_times_be = tal_arr(failonion, u8, 0);
912+
towire_u32(&hold_times_be, hold_times);
913+
memcpy(failonion->attr_data->htlc_hold_time, hold_times_be, HOLD_TIME_LEN);
914+
add_hmacs_to_attribution_data(failonion, shared_secret);
915+
}
916+
917+
static u8 *verify_attr_data(struct onionreply *reply,
918+
int pos,
919+
const struct secret *shared_secret)
920+
{
921+
struct secret um_key;
922+
struct hmac hmac;
923+
struct crypto_auth_hmacsha256_state state;
924+
u8 expected_hmac[4], actual_hmac[4];
925+
926+
subkey_from_hmac("um", shared_secret, &um_key);
927+
928+
hmac_start(&state, um_key.data, sizeof(um_key.data));
929+
hmac_update(&state, reply->contents, tal_bytelen(reply->contents));
930+
hmac_update(&state, reply->attr_data->htlc_hold_time, (pos + 1) * HOLD_TIME_LEN);
931+
write_downstream_hmacs(&state, pos, reply->attr_data->truncated_hmac);
932+
hmac_done(&state, &hmac);
933+
memcpy(expected_hmac, hmac.bytes, TRUNC_HMAC_LEN);
934+
935+
/* Compare with actual index. */
936+
int hmac_idx = MAX_HOPS - pos - 1;
937+
memcpy(actual_hmac, reply->attr_data->truncated_hmac + hmac_idx * TRUNC_HMAC_LEN, TRUNC_HMAC_LEN);
938+
939+
if (memcmp(actual_hmac, expected_hmac, 4) != 0) {
940+
return NULL;
941+
}
942+
943+
return tal_dup_arr(reply, u8, reply->attr_data->htlc_hold_time, 4, 0);
944+
}
945+
845946
u8 *unwrap_onionreply(const tal_t *ctx,
846947
const struct secret *shared_secrets,
847948
const int numhops,
@@ -852,9 +953,9 @@ u8 *unwrap_onionreply(const tal_t *ctx,
852953
const u8 *cursor;
853954
size_t max;
854955
u16 msglen;
855-
856-
r = new_onionreply(tmpctx, reply->contents);
956+
r = new_onionreply(tmpctx, reply->contents, reply->attr_data);
857957
*origin_index = -1;
958+
int attr_hop_count = numhops > 20 ? 20 : numhops;
858959

859960
for (int i = 0; i < numhops; i++) {
860961
struct secret key;
@@ -871,6 +972,36 @@ u8 *unwrap_onionreply(const tal_t *ctx,
871972
cursor = r->contents;
872973
max = tal_count(r->contents);
873974

975+
/* Check attribution error HMACs, If present. */
976+
if (r->attr_data) {
977+
if (i < attr_hop_count) {
978+
int pos = attr_hop_count - i - 1;
979+
u8 *hop_time = verify_attr_data(r, pos, &shared_secrets[i]);
980+
if (hop_time) {
981+
/* Shift Left */
982+
memmove(r->attr_data->htlc_hold_time, &r->attr_data->htlc_hold_time[HOLD_TIME_LEN], HOLD_TIME_LEN * (MAX_HOPS - 1));
983+
984+
int src_index = MAX_HOPS;
985+
int dest_index = 1;
986+
int copy_len = MAX_HOPS - 1;
987+
988+
for (int i = 0; i < MAX_HOPS - 1; i++) {
989+
memmove(&r->attr_data->truncated_hmac[dest_index * TRUNC_HMAC_LEN],
990+
&r->attr_data->truncated_hmac[src_index * TRUNC_HMAC_LEN],
991+
copy_len * TRUNC_HMAC_LEN);
992+
993+
src_index += copy_len;
994+
dest_index += copy_len + 1;
995+
copy_len -= 1;
996+
}
997+
// printf("Hop time at position %d: %s \n", i, tal_hexstr(tmpctx, hop_time, 4));
998+
} else {
999+
/* FIXME: Add logging */
1000+
// printf("Invalid HMAC at pos: %d", i);
1001+
}
1002+
}
1003+
}
1004+
8741005
fromwire_hmac(&cursor, &max, &hmac);
8751006
/* Too short. */
8761007
if (!cursor)

common/sphinx.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ struct node_id;
1818
#define ROUTING_INFO_SIZE 1300
1919
#define TOTAL_PACKET_SIZE(payload) (VERSION_SIZE + PUBKEY_SIZE + (payload) + HMAC_SIZE)
2020

21+
#define HOLD_TIME_LEN 4
22+
#define MAX_HOPS 20
23+
#define HMAC_COUNT 210
24+
#define TRUNC_HMAC_LEN 4
25+
2126
struct onionpacket {
2227
/* Cleartext information */
2328
u8 version;
@@ -241,6 +246,7 @@ struct onionpacket *sphinx_decompress(const tal_t *ctx,
241246
const struct sphinx_compressed_onion *src,
242247
const struct secret *shared_secret);
243248

249+
void update_attributable_data(struct onionreply *failonion, u32 hold_times, struct secret *shared_secret);
244250
/**
245251
* Use ECDH to generate a shared secret from a privkey and a pubkey.
246252
*

common/test/run-blindedpath_onion.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ void fromwire_sciddir_or_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED
7676
struct sciddir_or_pubkey *sciddpk UNNEEDED)
7777
{ fprintf(stderr, "fromwire_sciddir_or_pubkey called!\n"); abort(); }
7878
/* Generated stub for new_onionreply */
79-
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
79+
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED, const struct attribution_data *attr_data TAKES UNNEEDED)
8080
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
8181
/* Generated stub for pubkey_from_node_id */
8282
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)

common/test/run-onion-message-test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ bool fromwire_channel_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
3131
void fromwire_node_id(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct node_id *id UNNEEDED)
3232
{ fprintf(stderr, "fromwire_node_id called!\n"); abort(); }
3333
/* Generated stub for new_onionreply */
34-
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
34+
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED, const struct attribution_data *attr_data TAKES UNNEEDED)
3535
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
3636
/* Generated stub for pubkey_from_node_id */
3737
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)

common/test/run-onion-test-vector.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ bool fromwire_tlv(const u8 **cursor UNNEEDED, size_t *max UNNEEDED,
8686
const char *mvt_tag_str(enum mvt_tag tag UNNEEDED)
8787
{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); }
8888
/* Generated stub for new_onionreply */
89-
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
89+
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED, const struct attribution_data *attr_data TAKES UNNEEDED)
9090
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
9191
/* Generated stub for node_id_from_hexstr */
9292
bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED)

common/test/run-route_blinding_onion_test.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ void fromwire_sciddir_or_pubkey(const u8 **cursor UNNEEDED, size_t *max UNNEEDED
3333
const char *mvt_tag_str(enum mvt_tag tag UNNEEDED)
3434
{ fprintf(stderr, "mvt_tag_str called!\n"); abort(); }
3535
/* Generated stub for new_onionreply */
36-
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
36+
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED, const struct attribution_data *attr_data TAKES UNNEEDED)
3737
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
3838
/* Generated stub for node_id_from_hexstr */
3939
bool node_id_from_hexstr(const char *str UNNEEDED, size_t slen UNNEEDED, struct node_id *id UNNEEDED)

common/test/run-sphinx-xor_cipher_stream.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ void hmac_update(crypto_auth_hmacsha256_state *state UNNEEDED,
9898
const void *src UNNEEDED, size_t slen UNNEEDED)
9999
{ fprintf(stderr, "hmac_update called!\n"); abort(); }
100100
/* Generated stub for new_onionreply */
101-
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED)
101+
struct onionreply *new_onionreply(const tal_t *ctx UNNEEDED, const u8 *contents TAKES UNNEEDED, const struct attribution_data *attr_data TAKES UNNEEDED)
102102
{ fprintf(stderr, "new_onionreply called!\n"); abort(); }
103103
/* Generated stub for pubkey_from_node_id */
104104
bool pubkey_from_node_id(struct pubkey *key UNNEEDED, const struct node_id *id UNNEEDED)

plugins/xpay/xpay.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ static void update_knowledge_from_error(struct command *aux_cmd,
580580
if (!tok)
581581
plugin_err(aux_cmd->plugin, "Invalid injectpaymentonion result '%.*s'",
582582
json_tok_full_len(error), json_tok_full(buf, error));
583-
reply = new_onionreply(tmpctx, take(json_tok_bin_from_hex(NULL, buf, tok)));
583+
reply = new_onionreply(tmpctx, take(json_tok_bin_from_hex(NULL, buf, tok)), NULL);
584584

585585
replymsg = unwrap_onionreply(tmpctx,
586586
attempt->shared_secrets,

wallet/test/run-wallet.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1218,6 +1218,9 @@ u8 *unwrap_onionreply(const tal_t *ctx UNNEEDED,
12181218
const struct onionreply *reply UNNEEDED,
12191219
int *origin_index UNNEEDED)
12201220
{ fprintf(stderr, "unwrap_onionreply called!\n"); abort(); }
1221+
/* Generated stub for update_attributable_data */
1222+
void update_attributable_data(struct onionreply *failonion UNNEEDED, u32 hold_times UNNEEDED, struct secret *shared_secret UNNEEDED)
1223+
{ fprintf(stderr, "update_attributable_data called!\n"); abort(); }
12211224
/* Generated stub for watch_opening_inflight */
12221225
void watch_opening_inflight(struct lightningd *ld UNNEEDED,
12231226
struct channel_inflight *inflight UNNEEDED)
@@ -2229,7 +2232,7 @@ static bool test_htlc_crud(struct lightningd *ld, const tal_t *ctx)
22292232
CHECK_MSG(
22302233
transaction_wrap(w->db, wallet_htlc_update(w, in.dbid, SENT_REMOVE_HTLC, &payment_key, 0, 0, NULL, NULL, &we_filled, in.key.id, in.key.channel, REMOTE, &in.payment_hash, in.cltv_expiry, in.msat)),
22312234
"Update HTLC with payment_key failed");
2232-
onionreply = new_onionreply(tmpctx, tal_arrz(tmpctx, u8, 100));
2235+
onionreply = new_onionreply(tmpctx, tal_arrz(tmpctx, u8, 100), NULL);
22332236
CHECK_MSG(
22342237
transaction_wrap(w->db, wallet_htlc_update(w, in.dbid, SENT_REMOVE_HTLC, NULL, 0, 0, onionreply, NULL, &we_filled, in.key.id, in.key.channel, REMOTE, &in.payment_hash, in.cltv_expiry, in.msat)),
22352238
"Update HTLC with failonion failed");

wire/test/run-peer-wire.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ struct msg_update_fail_htlc {
202202
struct channel_id channel_id;
203203
u64 id;
204204
u8 *reason;
205+
206+
struct tlv_update_fail_htlc_tlvs *tlvs;
205207
};
206208
struct msg_channel_announcement {
207209
secp256k1_ecdsa_signature node_signature_1;
@@ -488,7 +490,8 @@ static void *towire_struct_update_fail_htlc(const tal_t *ctx,
488490
return towire_update_fail_htlc(ctx,
489491
&s->channel_id,
490492
s->id,
491-
s->reason);
493+
s->reason,
494+
s->tlvs);
492495
}
493496

494497
static struct msg_update_fail_htlc *fromwire_struct_update_fail_htlc(const tal_t *ctx, const void *p)
@@ -498,7 +501,8 @@ static struct msg_update_fail_htlc *fromwire_struct_update_fail_htlc(const tal_t
498501
if (!fromwire_update_fail_htlc(ctx, p,
499502
&s->channel_id,
500503
&s->id,
501-
&s->reason))
504+
&s->reason,
505+
&s->tlvs))
502506
return tal_free(s);
503507
return s;
504508

@@ -784,12 +788,19 @@ static bool announcement_signatures_eq(const struct msg_announcement_signatures
784788
return eq_upto(a, b, short_channel_id) &&
785789
short_channel_id_eq(a->short_channel_id, b->short_channel_id);
786790
}
791+
static bool tlv_update_fail_htlc_eq(const struct tlv_update_fail_htlc_tlvs_attribution_data *a,
792+
const struct tlv_update_fail_htlc_tlvs_attribution_data *b)
793+
{
794+
return eq_field(a, b, htlc_hold_times)
795+
&& eq_field(a, b, truncated_hmacs);
796+
}
787797

788798
static bool update_fail_htlc_eq(const struct msg_update_fail_htlc *a,
789799
const struct msg_update_fail_htlc *b)
790800
{
791801
return eq_with(a, b, id)
792-
&& eq_var(a, b, reason);
802+
&& eq_var(a, b, reason)
803+
&& eq_tlv(a, b, attribution_data, tlv_update_fail_htlc_eq);
793804
}
794805

795806
static bool tlv_splice_info_eq(const struct tlv_commitment_signed_tlvs_splice_info *a,
@@ -1015,12 +1026,15 @@ int main(int argc, char *argv[])
10151026

10161027
memset(&ufh, 2, sizeof(ufh));
10171028
ufh.reason = tal_arr(ctx, u8, 2);
1018-
memset(ufh.reason, 2, 2);
1029+
ufh.tlvs = tlv_update_fail_htlc_tlvs_new(tmpctx);
1030+
ufh.tlvs->attribution_data = tal(ctx, struct tlv_update_fail_htlc_tlvs_attribution_data);
1031+
memset(ufh.tlvs->attribution_data, 3, sizeof(struct tlv_update_fail_htlc_tlvs_attribution_data));
1032+
memset(ufh.reason, 2, 2);
10191033

10201034
msg = towire_struct_update_fail_htlc(ctx, &ufh);
10211035
ufh2 = fromwire_struct_update_fail_htlc(ctx, msg);
10221036
assert(update_fail_htlc_eq(&ufh, ufh2));
1023-
test_corruption(&ufh, ufh2, update_fail_htlc);
1037+
test_corruption_tlv(&ufh, ufh2, update_fail_htlc);
10241038

10251039
memset(&cs, 2, sizeof(cs));
10261040
cs.htlc_signature = tal_arr(ctx, secp256k1_ecdsa_signature, 2);

0 commit comments

Comments
 (0)