diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:43:41 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-26 22:43:41 +0000 |
| commit | 3fe9280fc11bc4ceaff3bb21533224b7b3e88da7 (patch) | |
| tree | 8179a95dbede51a2b3910b3c0bacae48c8c3b2d0 /src/test_btfaddr.cpp | |
| parent | bbe04dc8c96b4c4766d2771d3438233bf82687c0 (diff) | |
btfaddr: reject descriptors whose onion endpoint is not signed
Upstream 1.2.19 signs the onion endpoint only under the optional sig3, and
VerifyDescriptor treated sig3 failure as 'drop the onion, accept the rest'.
Any relay could therefore strip an onion endpoint down to a rendezvous-only
descriptor (or, under /oniononly, cut the peer off) by deleting one field.
Now an onion field must arrive with a valid sig3 over the V3 digest or the
whole descriptor is rejected. Honest 1.2.19 writers always emit both fields,
so interop is preserved. The full two-field strip still verifies -- it is
indistinguishable from a peer that never had an onion -- and needs the onion
bound into the primary sig, which only upstream can do without breaking every
1.2.18/1.2.19 verifier; reported as an upstream protocol defect.
test_btfaddr flips the two upstream checks that enshrined the soft drop and
adds unsigned-onion and full-strip cases.
Diffstat (limited to 'src/test_btfaddr.cpp')
| -rw-r--r-- | src/test_btfaddr.cpp | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/src/test_btfaddr.cpp b/src/test_btfaddr.cpp index 8de426e..9d33d63 100644 --- a/src/test_btfaddr.cpp +++ b/src/test_btfaddr.cpp | |||
| @@ -133,12 +133,23 @@ int main() | |||
| 133 | std::string d3 = btf::SignDescriptor(ctx, sk, ENC_HEX, "node-1", 5, ONION); | 133 | std::string d3 = btf::SignDescriptor(ctx, sk, ENC_HEX, "node-1", 5, ONION); |
| 134 | json j3 = json::parse(d3); | 134 | json j3 = json::parse(d3); |
| 135 | j3["onion"] = "bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde.onion:8433"; | 135 | j3["onion"] = "bcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcde.onion:8433"; |
| 136 | CHECK(btf::VerifyDescriptor(ctx, j3.dump(), pk, o) && o.onion.empty(), | 136 | CHECK(!btf::VerifyDescriptor(ctx, j3.dump(), pk, o), |
| 137 | "tampered onion ignored rather than trusted"); | 137 | "tampered onion rejects the descriptor"); |
| 138 | json j4 = json::parse(d3); | 138 | json j4 = json::parse(d3); |
| 139 | j4["sig3"] = std::string(128, '0'); | 139 | j4["sig3"] = std::string(128, '0'); |
| 140 | CHECK(btf::VerifyDescriptor(ctx, j4.dump(), pk, o) && o.onion.empty(), | 140 | CHECK(!btf::VerifyDescriptor(ctx, j4.dump(), pk, o), |
| 141 | "bad onion signature ignored rather than trusted"); | 141 | "bad onion signature rejects the descriptor"); |
| 142 | json j5 = json::parse(d3); | ||
| 143 | j5.erase("sig3"); | ||
| 144 | CHECK(!btf::VerifyDescriptor(ctx, j5.dump(), pk, o), | ||
| 145 | "unsigned onion rejects the descriptor"); | ||
| 146 | // Removing both fields still verifies: the result is indistinguishable | ||
| 147 | // from a peer that never had an onion endpoint. That residual strip | ||
| 148 | // needs the onion bound into the primary sig, an upstream change. | ||
| 149 | json j6 = json::parse(d3); | ||
| 150 | j6.erase("onion"); j6.erase("sig3"); | ||
| 151 | CHECK(btf::VerifyDescriptor(ctx, j6.dump(), pk, o) && o.onion.empty(), | ||
| 152 | "full strip indistinguishable from no-onion (documented residual)"); | ||
| 142 | } | 153 | } |
| 143 | 154 | ||
| 144 | printf("full_resolve_chain_from_address\n"); | 155 | printf("full_resolve_chain_from_address\n"); |