Skip to content

Ban unicode control characters from utf8 fields - #1341

Open
t-bast wants to merge 4 commits into
lightning:masterfrom
t-bast:disallow-null-chars
Open

Ban unicode control characters from utf8 fields#1341
t-bast wants to merge 4 commits into
lightning:masterfrom
t-bast:disallow-null-chars

Conversation

@t-bast

@t-bast t-bast commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

As discussed in #1260, we should ban NULL and other control characters in utf8 fields: there's no valid use-case for them.

We also make sure that feature bits MUST be minimally-encoded everywhere instead of being lenient for node_announcement for no good reason.

@morehouse morehouse left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it also make sense to call out that the receiving node MAY reject gossip with non-conforming alias or features fields?

Eclair and LND in particular may want to reject such gossip during decoding since they currently don't preserve the underlying bytes of some non-conforming fields for signature verification (ACINQ/eclair#3314, lightningnetwork/lnd#10835).

@t-bast

t-bast commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator Author

Good idea, I added explicit requirements on the receiving side in f160681

@morehouse morehouse left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Comment thread 01-messaging.md Outdated

@tnull tnull left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do wonder if it would be worth to provide test vectors for this? Maybe they could be generated by reusing the script we recently added to LDK, see https://github.com/lightningdevkit/rust-lightning/pull/4605/changes#diff-af90f55a5f5e4af60b13fc942aee60e49311cb223f15bccdf373117b4aabc3df

As discussed in lightning#1260, we should ban NULL and other control characters
in utf8 fields, along with characters that don't provide any value in
node aliases: there's no valid use-case for them.

Unfortunately, unicode cannot provide static lists of characters in each
category, since it is open to extension. We thus more strongly restrict
writers, and set reader restrictions that are more stable.

We also make sure that feature bits MUST be minimally-encoded everywhere
instead of being lenient for `node_announcement` for no good reason.
@t-bast
t-bast force-pushed the disallow-null-chars branch from f160681 to dcd957d Compare June 8, 2026 12:32

@kristapsk kristapsk left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Concept ACK

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Should probably do the same for BOLT 11/12 descriptions/memos/etc.

@erickcestari

Copy link
Copy Markdown
Contributor

Does it also make sense to call out that the receiving node MAY reject gossip with non-conforming alias or features fields?

Eclair and LND in particular may want to reject such gossip during decoding since they currently don't preserve the underlying bytes of some non-conforming fields for signature verification (ACINQ/eclair#3314, lightningnetwork/lnd#10835).

Probably a follow up PR, but I wonder if we should expand this to any features field at the point it sits under a signature (or maybe all features fields?) . That's node_announcement, channel_announcement, BOLT 11 invoices, and the BOLT 12 invoice_request / invoice merkle roots (which mirror offer_features). A standalone offer is unsigned, so offer_features only needs byte-preservation once it's carried into a signed message.

t-bast added 3 commits July 20, 2026 12:25
Those test vectors contain a valid signature.
Only the alias is invalid.
Note that we already require that features are minimally-encoded.
And require that features are minimally-encoded.
@t-bast

t-bast commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

I do wonder if it would be worth to provide test vectors for this?

I've added a few test vectors in 403f409. I don't think we can or should be exhaustive, we only need to nudge implementers to make sure they've banned the right categories, but if you want me to add other specific test vectors, don't hesitate to generate them and I'll add them!

@t-bast

t-bast commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Should probably do the same for BOLT 11/12 descriptions/memos/etc.

Some of them are already implicitly captured by the fact that the requirements have been added directly on the utf8 common type in Bolt1, but I've added it explicitly in many places in e686eef and b10ea05

@t-bast

t-bast commented Jul 20, 2026

Copy link
Copy Markdown
Collaborator Author

Probably a follow up PR, but I wonder if we should expand this to any features field at the point it sits under a signature (or maybe all features fields?) . That's node_announcement, channel_announcement, BOLT 11 invoices, and the BOLT 12 invoice_request / invoice merkle roots (which mirror offer_features). A standalone offer is unsigned, so offer_features only needs byte-preservation once it's carried into a signed message.

I agree, some of those already mentioned that features must be minimally-encoded. I've added explicit requirements where they were missing in e686eef and b10ea05.

As a rule of thumb, features must always be minimally encoded. It's just that we initially didn't include this requirement in the very early days of lightning and wanted to preserve backwards-compatibility while implementations ensured that they minimally encoded features. Nowadays, every implementation should have long shipped versions that minimally encode all the time, so we can reject anything that isn't minimally encoded.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I assume you've run a quick pass on the current gossip db to make sure ~everything is minimal?

@t-bast

t-bast commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator Author

Yes, I haven't found any gossip where features are not minimally encoded on mainnet today.
However, I've found 9 nodes with invalid alias 😮‍💨 :

  • alias=⚡ Snare Trap ​⚡​ remoteNodeId=02a0cbab2e273c3e72004d115f1be107b47e548b0969cbe40fc3e8bf6c7bc45991
  • alias=🍊🐈‍⬛ Meow²¹⚡ remoteNodeId=029788dc40dae08c241e9cae11c6bbd223f47cf4746225dba5a7b7f87b508fe4b0
  • alias=🫡​💯​🏁​ remoteNodeId=03de2de00fdc509e929d2621bcf6976fd72f685c863aab50a06faecaaf7f8f4f73
  • alias=🤖RoboSats⚡ 👨‍💻 remoteNodeId=037ff12b6a4e4bcb4b944b6d20af08cdff61b3461c1dff0d00a88697414d891bc7
  • alias=⚡​AquiTemBitcoin⚡​ remoteNodeId=036b7ad803b5ba34a3eb39ef501ce1aeb700084c4cf11ba64ca57654eddcb4ecda
  • alias=⚡​j4b4t0⚡​ remoteNodeId=03e6dfa2415b9c4dfa3cbadbcf0b2a4e75b9e6512be228ced6e2177316f3bf6099
  • alias=​🐸​Puitenkuil remoteNodeId=0348b8addc7183e7264130010c45e65f091c35bad81f06379f7e8009e0c426c294
  • alias=EdZB.Node 🏴‍☠️ remoteNodeId=03af0aec5069083dfe72a94cf388a6a1a3bcb2ddb53437cd34f55752ba93df03bd
  • alias=🧙🏿‍♂️ remoteNodeId=03ce4c1a9a11910b8d6c1a8028b94b99d2dade8c9380736dce5c640997ff7e3d75

They're rather small nodes (but not inactive) except for the last one, with has 570,000,000 sats (~370,000$) of public capacity (8 channels).

If anyone knows who owns these nodes, please get them to change their alias, otherwise their node announcement will stop propagating soon!

Or maybe I messed up my eclair code and I'm too trigger-happy in which case it would be nice to have another implementation test those announcements:

9b6cc25ecc611be9c208ec92a8511f11e295c7e8d16bc5319a8a1a9ee39bd400162e8c5b17a037e759693db4459dd7b44a74311d3d8a6fde53fa5b1b2218a18900fd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008020008a8251a167a3b8c302a0cbab2e273c3e72004d115f1be107b47e548b0969cbe40fc3e8bf6c7bc45991ff9900e29aa120536e617265205472617020e2808be29aa1e2808b00000000000000000026047a158c3784abf5cb05f6e9cfa7879d35d596179e3a81d9bef0d9f5caa0f6eed0ea45032607
fee258049c0c1b509af8b7f38802c4a6c0e9cf6abd1fd1410876478e82d6dc134c4f50cf3494c52790c85f6d8234e176f39304eb18df4839e8b9fb417a993b0900fd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000008010888a8a51a16a60645a029788dc40dae08c241e9cae11c6bbd223f47cf4746225dba5a7b7f87b508fe4b0ff9112f09f8d8af09f9088e2808de2ac9b204d656f77c2b2c2b9e29aa10000000000000026044e58a09535a593f5ca8bc4e0de44cfba71e32466962c7344daf0af2058bf001c0b09032607
d05c4b2f17a3634a33b3b1adf66f10e978723cb81446bddb9529af4f2884a19164118719fc81305dea4cd260881f8c3bebaa0efcd776fe15394d7e41067f313a00fd800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000802000888a52a16678606c03de2de00fdc509e929d2621bcf6976fd72f685c863aab50a06faecaaf7f8f4f73ff9900f09faba1e2808bf09f92afe2808bf09f8f81e2808b0000000000000000000000002604cdb94deceb64d9ae3e719b13df2464d619b86e58edaf50df532c703c810877f196da032607
f39e50752a3d914b9bfba896db210dd1d2c2cd4ff3ec909f9ecd897979f278c577686978b5ac17e97c14665b28a7a9730be82f25da5524a70167aab32be3abf700fd800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000802000888a52a165ada311037ff12b6a4e4bcb4b944b6d20af08cdff61b3461c1dff0d00a88697414d891bc71976d2f09fa496526f626f53617473e29aa120f09f91a8e2808df09f92bb0000000000002604f22b3fff87e6ea459c8c7728718ae3725b69f95aa96f97d63cc0388ac7ff1f75a4cd032607
285c3f15c7135f0522a8709d1a712668e5265d5da911c7be7c5c87bf4295de6f5356f7fbdb990f93202f219cc5437e9876102facd2c48af75f7edac83e7f07e300fd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008020008a8251a167b4a61d036b7ad803b5ba34a3eb39ef501ce1aeb700084c4cf11ba64ca57654eddcb4ecdaff9900e29aa1e2808b4171756954656d426974636f696ee29aa1e2808b000000000000002604040f7b69e706eef66fd9738022600233fbc5f1577df10923043474062968cc5cdbe9032607
97824577192e81e2b621e59809fe82088450648e79321cd2464344be1ea783b64bd8cde6de583546ae77466ac41f339fc24509db5267f193bd7674e11e70285900fd80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000002000020000000000000000000000002088a0088a8a51a16961ff3803e6dfa2415b9c4dfa3cbadbcf0b2a4e75b9e6512be228ced6e2177316f3bf6099ffe135e29aa1e2808b6a3462347430e29aa1e2808b00000000000000000000000000000026044e09667919e8580e2c4b604ee2f19ac7b743ab8c4077a0dc1a0d02394be70134209e032607
17e2f19e094d635c51419d0028949f934ac67ff9793b01da9d69a06c9e427ea655857215bc6873f8c610c4ccc9a9e38c602ad9ea2852892654243b4b56515e3300fd80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000200000000000000000002000020000000000000000000000002088a0008a8a51a168b039ee0348b8addc7183e7264130010c45e65f091c35bad81f06379f7e8009e0c426c2946aa84fe2808bf09f90b8e2808b50756974656e6b75696c000000000000000000000000002604423addbc02b6f2d491ccd098efdd3f318dd72279cadc508603e086376d24c4d3575f032607
6a8d5c8712367139f89ebe3b69772c0a6e5d84466c719b1fac203de2697d03cf5cc637a8b9bbc93b5bbf60a2259422405ef427a5aa2af71baaf731b6f9c235c300fd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000020008a8251a168d06a4803af0aec5069083dfe72a94cf388a6a1a3bcb2ddb53437cd34f55752ba93df03bd3399ff45645a422e4e6f646520f09f8fb4e2808de298a0efb88f000000000000000000002604382011a399c8be9e1c9d4169c9028bf6f5685e686d1dc731f4334f20d4de259fa328032607
54417b86d0d62a023d86568df6e6707f1e85769afc496e58023828d0955ef08c7f8da20721f5148e9e27a4861d6e60ee179228d390916c49f5a7e6af4389761200fd8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000008020088a8a51a1698de93503ce4c1a9a11910b8d6c1a8028b94b99d2dade8c9380736dce5c640997ff7e3d755741d9f09fa799f09f8fbfe2808de29982efb88f0000000000000000000000000000000072046af071c634ea09bd6b074f1f456aa0a7714e73bd30db8a2bbc7a39e543b3aed0b13c032607046af071c634ea09bd6b074f1f456aa0a7714e73bd30db8a2bbc7a39e543b3aed0b13c032607046af071c634ea09bd6b074f1f456aa0a7714e73bd30db8a2bbc7a39e543b3aed0b13c032607

@Roasbeef

Copy link
Copy Markdown
Collaborator

Makes sense to standardize the strictness. As far as the nodes with now invalid aliases, I think we'll go with a route where we'll silently re-write the the alias to be compliant with these new rules.

@erickcestari

erickcestari commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

I think we'll go with a route where we'll silently re-write the the alias to be compliant with these new rules.

Wouldn't rewriting the alias invalidate the node_announcement signature?

@t-bast

t-bast commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator Author

Wouldn't rewriting the alias invalidate the node_announcement signature?

What roasbeef means here is not that intermediate nodes would rewrite the node_announcements they receive (which indeed would invalidate the signature), but rather that implementations will rewrite the alias they read from the node operator's configuration to remove invalid characters and create a new node_announcement with a sanitized alias. Does that make more sense?

@vincenzopalazzo

Copy link
Copy Markdown
Contributor

Sorry, yesterday I was not able to speak up during the meeting due to a microphone issue!

I think that if this can impact the protocol, every implementation should do a check during the startup and emit a warning or not start at all if this can impact channel availability. In my experience, some node operators do not pay attention to the node status (now it should be easier with AI tho)

Otherwise Concept ACK for me

@nGoline

nGoline commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

cACK

t-bast added a commit to ACINQ/eclair that referenced this pull request Jul 29, 2026
We ban `NULL` and other control characters in utf8 fields: there's no
valid use-case for them.

We also make sure that feature bits MUST be minimally-encoded everywhere
instead of being lenient for no good reason.

See lightning/bolts#1341
@vincenzopalazzo

vincenzopalazzo commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

While reviewing LDK's PrintableString (the local equivalent of these rules) we found a residual gap this PR doesn't currently cover:

U+2028 LINE SEPARATOR (Zl) and U+2029 PARAGRAPH SEPARATOR (Zp) sit in the Z category, not C*, so they're outside both the writer/reader language here and char::is_control()/Cc. Many terminals and log viewers still treat them as hard line breaks, so a peer-controlled alias, BOLT 11 d, or BOLT 12 description/issuer/payer_note can still inject forged log lines — same threat model as the Cf case already covered.

Suggest also banning Zl/Zp (not all of Z*Zs contains U+0020 SPACE). Happy to add a U+2028 test vector if useful. LDK side: https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/compare/main...vincenzopalazzo:2026-08-04-printablestring-zl-zp

@t-bast

t-bast commented Aug 5, 2026

Copy link
Copy Markdown
Collaborator Author

Suggest also banning Zl/Zp (not all of Z* — Zs contains U+0020 SPACE). Happy to add a U+2028 test vector if useful. LDK side: https://git.rust-bitcoin.org/lightningdevkit/rust-lightning/compare/main...vincenzopalazzo:2026-08-04-printablestring-zl-zp

Thanks, good catch! Can you provide a test vector for this? I'll add it with the removed of Zl/Zp in a new commit.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

The test from that branch was ok\u{2028}forged\u{2029}more

@vincenzopalazzo

Copy link
Copy Markdown
Contributor

Here is a commit you can cherry-pick: vincenzopalazzo@94de118

git fetch https://github.com/vincenzopalazzo/bolts 2026-08-12-zl-zp-test-vectors && git cherry-pick 94de1185d33dda37091dd5d77471e5a0ebb3ca17

It sits on top of b10ea05 and does two things:

  • adds Zl/Zp to the category lists in BOLT 1, 7, 11 and 12, so writers get "Other" (C*), "Zl" or "Zp" General Categories and readers get "Cc", "Cf", "Cs", "Co", "Zl", or "Zp"
  • adds the two node_announcement test vectors:
  {
    "name": "Alias containing a line separator (Zl)",
    "alias": "lightning\u2028rocks",
    "announcement": "0101e7965f97f2e32a81eff5f58936b1d201b3c62f33c67d383ecf5b7cdba062fd0f6357d2914084953042ec18f30ae37cf3a65ac2a5839337b53004043398c7d88a000067b64b00034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0102036c696768746e696e67e280a8726f636b730000000000000000000000000000000000"
  },
  {
    "name": "Alias containing a paragraph separator (Zp)",
    "alias": "lightning\u2029rocks",
    "announcement": "0101911380dd0f32972e3d4cf79973b8d857966069538517e93bf9a543ed540763037195800c1f33b45ed908dbe45b66e08941ea61c6e9247a2d03e459ea5127f761000067b64b00034f355bdcb7cc0af728ef3cceb9615d90684bb5b2ca5f859ab0f0b704075871aa0102036c696768746e696e67e280a9726f636b730000000000000000000000000000000000"
  }

Same construction as the existing four: priv=0x1111...11, timestamp=1740000000, rgb=010203, empty features, no addresses, RFC6979 signature over the double-SHA256 of everything after the signature field. I regenerated your four vectors byte for byte first to make sure these are drop-in. Alias bytes are 6c696768746e696e67 e280a8 726f636b73 and 6c696768746e696e67 e280a9 726f636b73, zero-padded to 32.

I included the rule text change in the same commit so it stays self-consistent, since the vectors are "MUST ignore" and without Zl/Zp in the reader lists there is no rule that rejects them. If you already have the text change locally, just take the test vector hunk in 07 and drop the rest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants