Rust workspace with 5 crates (mesh-types, mesh-crypto, mesh-network, mesh-validator, mesh-wallet), PROJECT_CONSTITUTION.md for CHORUS automated ingestion, and the full MESH protocol specification suite. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
634 lines
28 KiB
Markdown
634 lines
28 KiB
Markdown
**MESH PROTOCOL**
|
||
|
||
*Security Addendum*
|
||
|
||
Attack Vector Analysis and Formal Mitigations
|
||
|
||
Errata and amendments to SPEC-003, SPEC-007, SPEC-008, SPEC-010
|
||
|
||
Version 0.1.1-draft \| March 2026
|
||
|
||
**Introduction**
|
||
|
||
This document identifies seven attack vectors against the MESH protocol
|
||
as specified in the v0.1.0 specification suite. For each attack, we
|
||
provide: the threat description, the root cause in the existing
|
||
specification, the formal mitigation, the specification amendments
|
||
required, and the mandatory test assertions.
|
||
|
||
All mitigations in this document are MANDATORY amendments to the
|
||
referenced specifications. An implementation that passes all v0.1.0 test
|
||
vectors but does not implement these mitigations is NON-CONFORMANT. The
|
||
version number of the specification suite is incremented to v0.1.1 upon
|
||
adoption of this addendum.
|
||
|
||
**Severity Classification**
|
||
|
||
-------------- ----------- -----------------------------------------------
|
||
**Severity** **Label** **Definition**
|
||
|
||
S1 CRITICAL Allows direct theft of funds or creation of
|
||
value from nothing. Protocol is broken without
|
||
mitigation.
|
||
|
||
S2 HIGH Allows economic griefing, denial of service
|
||
against specific users, or degrades privacy
|
||
guarantees.
|
||
|
||
S3 MEDIUM Allows minor economic advantage or information
|
||
leakage under specific conditions.
|
||
-------------- ----------- -----------------------------------------------
|
||
|
||
**AV-001: Free Option PTLC Griefing Attack**
|
||
|
||
**Severity: S2 (HIGH)**
|
||
|
||
**1.1 Threat Description**
|
||
|
||
A malicious sender initiates a multi-hop payment via PTLCs (SPEC-008
|
||
§8.4) with a long expiry. The Prepare packet locks the connector's
|
||
liquidity for the duration of the expiry. The sender then monitors the
|
||
market and decides whether to fulfill or abandon the payment based on
|
||
whether the exchange rate has moved in their favour.
|
||
|
||
This creates a free financial option: the connector bears the full
|
||
market risk (their liquidity is locked at a fixed rate) while the sender
|
||
has the optionality to walk away at zero cost. Over millions of
|
||
transactions, a systematic attacker could extract significant value from
|
||
connectors.
|
||
|
||
**1.2 Root Cause**
|
||
|
||
SPEC-008 §8.2.1 defines an expiry field on MeshPacket but imposes no
|
||
constraint on its maximum value and no cost for locking connector
|
||
liquidity. The specification is silent on the relationship between
|
||
expiry duration and economic cost.
|
||
|
||
**1.3 Mitigation**
|
||
|
||
Three complementary mechanisms:
|
||
|
||
**1.3.1 Maximum Expiry Decay Per Hop**
|
||
|
||
Each connector along a payment path MUST reduce the expiry by at least a
|
||
minimum decrement. This limits the total lock-up time and ensures that
|
||
senders cannot impose arbitrarily long lock-ups.
|
||
|
||
> **SPEC-008 AMENDMENT §8.4.5 (NEW):** Each connector MUST set the
|
||
> outgoing Prepare packet's expiry to at most: outgoing_expiry =
|
||
> incoming_expiry - T_hop, where T_hop is a per-connector configuration
|
||
> parameter with a RECOMMENDED minimum of 30 seconds. A connector MUST
|
||
> reject an incoming Prepare if incoming_expiry - current_time \< T_hop
|
||
> (insufficient time to complete the hop). This creates a hard ceiling
|
||
> on total path expiry: max_total_expiry = T_hop × max_path_length. With
|
||
> T_hop = 30s and max_path_length = 10 hops, the maximum lock-up is 5
|
||
> minutes.
|
||
|
||
**1.3.2 Locking Fee**
|
||
|
||
Connectors MAY charge a time-value locking fee that is deducted from the
|
||
payment regardless of whether it is fulfilled or rejected. This converts
|
||
the free option into a priced option.
|
||
|
||
> **SPEC-008 AMENDMENT §8.5.1 (NEW):** A connector MAY specify a
|
||
> locking_fee_rate in its published rate information, expressed as basis
|
||
> points per second of lock-up time. The locking fee is: locking_fee =
|
||
> amount × locking_fee_rate × (expiry - current_time) / 10000. The
|
||
> locking fee is deducted from the transferred amount. If the payment is
|
||
> fulfilled, the connector receives the locking fee plus the spread. If
|
||
> the payment expires, the connector retains the locking fee. The
|
||
> sender's wallet MUST account for locking fees when calculating the
|
||
> total cost of a payment.
|
||
|
||
**1.3.3 Sender Reputation**
|
||
|
||
Connectors SHOULD maintain a local reputation score for senders based on
|
||
their fulfillment rate. Senders who frequently let payments expire
|
||
receive lower priority and wider spreads.
|
||
|
||
> **SPEC-008 AMENDMENT §8.5.2 (NEW):** Connectors SHOULD track the ratio
|
||
> of fulfilled to expired Prepare packets per sender public key. A
|
||
> connector MAY reject Prepare packets from senders whose fulfillment
|
||
> rate falls below a configurable threshold (RECOMMENDED: 80% over the
|
||
> last 1000 packets).
|
||
|
||
**1.4 Test Assertions**
|
||
|
||
1. Assert that a connector rejects an incoming Prepare with expiry \>
|
||
current_time + T_hop × max_path_length.
|
||
|
||
2. Assert that each hop reduces the outgoing expiry by at least T_hop.
|
||
|
||
3. Assert that a payment path with 10 hops and T_hop = 30s has a
|
||
maximum total lock-up of 300 seconds.
|
||
|
||
4. Assert that the locking fee is correctly deducted from the amount
|
||
when the payment expires without fulfillment.
|
||
|
||
5. Assert that a sender with a 50% fulfillment rate is rejected by a
|
||
connector with an 80% threshold.
|
||
|
||
**AV-002: Negative Fee Range Proof Bypass**
|
||
|
||
**Severity: S1 (CRITICAL)**
|
||
|
||
**2.1 Threat Description**
|
||
|
||
A sender constructs a transition where the amount commitment is valid
|
||
(positive value with valid range proof), but the fee commitment encodes
|
||
a negative value. If the conservation check only verifies that
|
||
Σ(recipient amounts) + fee = debit amount on the commitment points, and
|
||
the fee range proof is missing or weak, the sender effectively increases
|
||
their balance by the absolute value of the negative fee.
|
||
|
||
Example: Sender has balance 1000. Sender creates a transfer of 1000 to
|
||
recipient, with fee = -500. Conservation check passes: 1000 + (-500) =
|
||
500. But the sender has only been debited 500 from a balance of 1000, so
|
||
they retain 500 extra. The recipient gets 1000. 500 units of value have
|
||
been created from nothing.
|
||
|
||
**2.2 Root Cause**
|
||
|
||
SPEC-002 §2.4.3 specifies RANGE_CHECK (rule 6) and CONSERVATION_CHECK
|
||
(rule 7) as separate steps. However, the specification text for
|
||
RANGE_CHECK states: "All range proofs MUST verify. This proves all
|
||
amounts are non-negative and within the valid range \[0, 2⁶⁴)." The word
|
||
"amounts" is ambiguous --- it could be read as referring only to the
|
||
recipient amounts, not the fee. This ambiguity is the attack surface.
|
||
|
||
**2.3 Mitigation**
|
||
|
||
Eliminate the ambiguity with an explicit enumeration of every commitment
|
||
that requires a range proof.
|
||
|
||
> **SPEC-002 AMENDMENT §2.4.3 Rule 6 (REVISED):** RANGE_CHECK: The
|
||
> following range proofs MUST be present and MUST verify: (a)
|
||
> transition.range_proof proves transition.amount is in \[0, 2⁶⁴). (b)
|
||
> transition.fee_proof proves transition.fee is in \[0, 2⁶⁴). (c) For
|
||
> each recipient r in transition.recipients: r.range_proof proves
|
||
> r.amount is in \[0, 2⁶⁴). If ANY of these proofs is absent, malformed,
|
||
> or fails verification, the validator MUST reject the transition with
|
||
> ERR_INVALID_RANGE_PROOF. There are no exceptions. A transition with
|
||
> zero fee MUST still include a valid range proof for the zero-value fee
|
||
> commitment.
|
||
>
|
||
> **SPEC-002 AMENDMENT §2.4.3 Rule 7 (CLARIFIED):** CONSERVATION_CHECK:
|
||
> The sum of all output commitments MUST equal the input commitment.
|
||
> Formally: C_amount = Σ(C_recipient_i) + C_fee. This is verified on the
|
||
> commitment points (elliptic curve point addition). The validator MUST
|
||
> verify this equation AFTER all range proofs have passed. The
|
||
> combination of range proofs (all values non-negative) and conservation
|
||
> (inputs = outputs) together guarantee that no value is created or
|
||
> destroyed.
|
||
|
||
**2.4 Test Assertions**
|
||
|
||
6. Assert that a transition with a valid amount range proof but a
|
||
missing fee range proof is rejected with ERR_INVALID_RANGE_PROOF.
|
||
|
||
7. Assert that a transition with a fee commitment to a negative value
|
||
(commitment point C_fee where opening reveals v \< 0) is rejected.
|
||
|
||
8. Assert that a transition with fee = 0 still requires a valid range
|
||
proof for the zero commitment.
|
||
|
||
9. Assert that a transition where Σ(outputs) + fee = input but fee is
|
||
negative is rejected (the range proof on fee catches this before the
|
||
conservation check).
|
||
|
||
10. Assert that crafting a Bulletproofs+ proof for a value outside \[0,
|
||
2⁶⁴) produces a proof that fails verification.
|
||
|
||
**AV-003: Causal Dependency Orphan Exploit**
|
||
|
||
**Severity: S1 (CRITICAL)**
|
||
|
||
**3.1 Threat Description**
|
||
|
||
A Byzantine account references a hash in causal_deps that is a
|
||
syntactically valid SHA3-256 digest but does not correspond to any
|
||
settlement certificate the validator has seen. If the validator only
|
||
checks the hash format (32 bytes, non-zero) without verifying that it
|
||
corresponds to a real, certified transition, the Byzantine account could
|
||
spend funds it never received.
|
||
|
||
Worse: if the "ghost" dependency references a fabricated certificate
|
||
that credits the attacker with value, the attacker creates money from
|
||
nothing by spending against a fictional incoming payment.
|
||
|
||
**3.2 Root Cause**
|
||
|
||
SPEC-002 §2.4.3 Rule 8 (CAUSAL_CHECK) states: "Every hash in causal_deps
|
||
MUST reference a transition that the validator has already processed and
|
||
certified." This is correct in intent but insufficiently precise about
|
||
what "processed and certified" means. The specification does not mandate
|
||
how the validator performs this lookup or what happens during network
|
||
partition when a validator may be behind.
|
||
|
||
**3.3 Mitigation**
|
||
|
||
> **SPEC-002 AMENDMENT §2.4.3 Rule 8 (REVISED):** CAUSAL_CHECK: For
|
||
> every hash h in transition.causal_deps, the validator MUST verify
|
||
> that: (a) h exists in the validator's local certificate store (a hash
|
||
> map keyed by transition hash, populated only when a valid
|
||
> SettlementCertificate is received and verified per SPEC-003 §3.3.2),
|
||
> AND (b) the referenced transition's effects (credit to the current
|
||
> sender) have been applied to the sender's AccountState. If any hash is
|
||
> not found in the certificate store, the validator MUST return
|
||
> ERR_UNKNOWN_DEPENDENCY. The validator MUST NOT attempt to fetch the
|
||
> missing certificate from the network during validation. If a
|
||
> dependency is genuinely missing (e.g., due to network delay), the
|
||
> sender must retry after the dependency has been disseminated to the
|
||
> validator.
|
||
|
||
Additionally, the specification must prevent the case where a sender
|
||
references a certified transition that does not actually credit them:
|
||
|
||
> **SPEC-002 AMENDMENT §2.4.3 Rule 8a (NEW):**
|
||
> DEPENDENCY_RELEVANCE_CHECK: For every hash h in
|
||
> transition.causal_deps, the validator MUST verify that the referenced
|
||
> transition either: (a) has the current sender as a recipient, OR (b)
|
||
> is a prior transition by the current sender (same sender public key).
|
||
> A transition that references an unrelated third-party certificate as a
|
||
> causal dependency MUST be rejected with ERR_IRRELEVANT_DEPENDENCY.
|
||
> This prevents an attacker from padding causal_deps with arbitrary
|
||
> valid hashes to confuse causal ordering analysis.
|
||
|
||
**3.4 Test Assertions**
|
||
|
||
11. Assert that a transition referencing a non-existent hash in
|
||
causal_deps is rejected with ERR_UNKNOWN_DEPENDENCY.
|
||
|
||
12. Assert that a transition referencing a real certificate hash that
|
||
does not credit the sender is rejected with
|
||
ERR_IRRELEVANT_DEPENDENCY.
|
||
|
||
13. Assert that a transition referencing a valid, relevant certificate
|
||
is accepted.
|
||
|
||
14. Assert that a validator does NOT fetch missing dependencies from the
|
||
network during the validation path (no side-effects during
|
||
validation).
|
||
|
||
**AV-004: Cross-Shard Double-Spend Race**
|
||
|
||
**Severity: S1 (CRITICAL) if sharding is incorrectly implemented;
|
||
MITIGATED BY DESIGN if spec is followed**
|
||
|
||
**4.1 Threat Description**
|
||
|
||
A Byzantine sender sends two conflicting transitions (same sequence
|
||
number, different recipients) to two different validator shards
|
||
simultaneously, hoping that each shard independently certifies a
|
||
different transition before the equivocation is detected.
|
||
|
||
**4.2 Root Cause**
|
||
|
||
SPEC-003 §3.6 defines the sharding function as shard_id =
|
||
hash(account_pubkey) mod num_shards. This is correct and ensures all
|
||
transitions for a given account go to the same shard. However, the
|
||
specification does not explicitly state that this is a SAFETY-CRITICAL
|
||
invariant, and does not specify what happens if an implementation uses a
|
||
different sharding function.
|
||
|
||
**4.3 Mitigation**
|
||
|
||
This attack is already mitigated by design in the specification, but the
|
||
safety-critical nature of the sharding invariant must be made explicit:
|
||
|
||
> **SPEC-003 AMENDMENT §3.6 (REVISED):** SHARDING SAFETY INVARIANT: All
|
||
> transitions for a given account MUST be validated by the same set of
|
||
> validators/shards. The shard assignment function is: shard_id =
|
||
> SHA3-256(account_pubkey)\[0..4\] mod num_shards, where \[0..4\] takes
|
||
> the first 4 bytes of the hash as a little-endian u32. This function
|
||
> MUST be deterministic, stateless, and identical across all validators.
|
||
> A validator MUST reject a VOTE_REQUEST if the sender's account does
|
||
> not map to a shard it is responsible for. An implementation that uses
|
||
> a different sharding function, or that allows a transition to be
|
||
> processed by a non-responsible shard, is NON-CONFORMANT and UNSAFE.
|
||
>
|
||
> **SPEC-003 AMENDMENT §3.6.1 (NEW):** CROSS-SHARD VERIFICATION: When a
|
||
> validator receives a SettlementCertificate for a recipient that maps
|
||
> to a different shard, it MUST verify the certificate (signatures,
|
||
> quorum) before crediting the recipient. The certificate provides proof
|
||
> that the sender's shard has validated and certified the debit. The
|
||
> receiving shard does not re-validate the sender's balance or sequence
|
||
> number --- it trusts the certificate as proof that the sender's shard
|
||
> has done so. This is safe because the sender's shard is the sole
|
||
> authority for the sender's account state.
|
||
|
||
**4.4 Test Assertions**
|
||
|
||
15. Assert that a validator rejects a VOTE_REQUEST if
|
||
hash(sender_pubkey) mod num_shards does not match the validator's
|
||
shard_id.
|
||
|
||
16. Assert that two conflicting transitions from the same sender (same
|
||
sequence, different content) sent to the correct shard produce an
|
||
equivocation proof.
|
||
|
||
17. Assert that a conflicting transition sent to the WRONG shard is
|
||
rejected before equivocation detection (at the shard routing check).
|
||
|
||
18. Assert that the sharding function is deterministic: the same public
|
||
key always produces the same shard_id regardless of when or where it
|
||
is computed.
|
||
|
||
**AV-005: Multi-Hop Dust Shaving**
|
||
|
||
**Severity: S3 (MEDIUM)**
|
||
|
||
**5.1 Threat Description**
|
||
|
||
A malicious connector in a multi-hop payment path exploits integer
|
||
division rounding to pocket small amounts of value ("dust") from each
|
||
transaction. When converting between asset types, the exchange
|
||
calculation amount_out = amount_in × rate.numerator / rate.denominator
|
||
produces a non-integer result that must be truncated. The connector
|
||
keeps the fractional remainder.
|
||
|
||
At 1 cent per transaction and 1 million transactions per day, this
|
||
yields \$10,000/day in stolen dust.
|
||
|
||
**5.2 Root Cause**
|
||
|
||
SPEC-002 §2.7 defines the Value Semiring's exchange operation as (a,
|
||
id₁) ⊗ rate = (a × rate.numerator / rate.denominator, id₂) but does not
|
||
specify the rounding rule or what happens to the remainder.
|
||
|
||
**5.3 Mitigation**
|
||
|
||
> **SPEC-002 AMENDMENT §2.7.1 (NEW):** EXCHANGE ROUNDING RULE: All
|
||
> exchange calculations MUST use the following formula: amount_out =
|
||
> floor(amount_in × rate.numerator / rate.denominator). The remainder
|
||
> is: remainder = amount_in × rate.numerator mod rate.denominator. The
|
||
> remainder MUST be returned to the sender (not retained by the
|
||
> connector). The sender's wallet MUST verify that amount_out +
|
||
> remainder (converted back to the source asset) exactly equals the
|
||
> original amount_in. This is enforced at the PTLC fulfillment stage:
|
||
> the sender only fulfills the PTLC if the received amount matches
|
||
> expectations within a sender-specified tolerance (max_slippage).
|
||
>
|
||
> **SPEC-008 AMENDMENT §8.2.1 (REVISED):** The MeshPacket Prepare
|
||
> message MUST include a new field: min_destination_amount (u128). The
|
||
> recipient (or the recipient-side connector) MUST reject the Prepare
|
||
> with ERR_BELOW_MINIMUM if the actual delivered amount is less than
|
||
> min_destination_amount. This shifts control to the sender: the sender
|
||
> specifies the minimum acceptable output, and if any connector along
|
||
> the path shaves more than the sender is willing to tolerate, the
|
||
> payment fails rather than succeeding with missing value.
|
||
|
||
**5.4 Test Assertions**
|
||
|
||
19. Assert that for an exchange with rate 3/7 and amount_in = 100, the
|
||
result is amount_out = floor(300/7) = 42, remainder = 300 mod 7 = 6.
|
||
|
||
20. Assert that the remainder is returned to the sender, not retained by
|
||
the connector.
|
||
|
||
21. Assert that a Prepare packet with min_destination_amount = 42 and
|
||
actual delivery of 41 is rejected with ERR_BELOW_MINIMUM.
|
||
|
||
22. Assert that integer overflow in the multiplication amount_in ×
|
||
rate.numerator is handled (use u256 intermediate or checked
|
||
arithmetic). If overflow would occur, the operation MUST fail with
|
||
ERR_OVERFLOW rather than silently truncating.
|
||
|
||
**AV-006: Epoch-Boundary Replay Attack**
|
||
|
||
**Severity: S1 (CRITICAL)**
|
||
|
||
**6.1 Threat Description**
|
||
|
||
A user collects VOTE_RESPONSE messages from validators in Epoch N but
|
||
does not broadcast the SettlementCertificate until Epoch N+1 (or later).
|
||
If validators in the new epoch accept certificates signed by the old
|
||
epoch's validators, the user could exploit a situation where: (a) a
|
||
validator was removed from the set in Epoch N+1 (perhaps for
|
||
misbehaviour), and its votes are now suspect, or (b) the validator set
|
||
has changed and the "2f+1 quorum" calculated against the new set may not
|
||
hold against the old set.
|
||
|
||
**6.2 Root Cause**
|
||
|
||
SPEC-002 §2.5.1 defines the SettlementCertificate with an epoch field
|
||
and states votes must be from validators in that epoch's set. However,
|
||
the specification does not define a grace period or cutoff for
|
||
cross-epoch certificate acceptance. The specification is silent on what
|
||
happens when a certificate arrives after an epoch boundary.
|
||
|
||
**6.3 Mitigation**
|
||
|
||
> **SPEC-003 AMENDMENT §3.3.2 (REVISED):** EPOCH VALIDATION FOR
|
||
> CERTIFICATES: When a validator receives a SettlementCertificate, it
|
||
> MUST perform the following epoch check: (a) If certificate.epoch ==
|
||
> current_epoch: accept and verify votes against the current validator
|
||
> set. (b) If certificate.epoch == current_epoch - 1: accept and verify
|
||
> votes against the PREVIOUS epoch's validator set. This provides a
|
||
> one-epoch grace period for certificates that were assembled just
|
||
> before an epoch transition. (c) If certificate.epoch \<
|
||
> current_epoch - 1: REJECT with ERR_CERTIFICATE_EXPIRED. Certificates
|
||
> more than one epoch old are not accepted. (d) If certificate.epoch \>
|
||
> current_epoch: REJECT with ERR_FUTURE_EPOCH. The validator does not
|
||
> accept certificates from future epochs.
|
||
>
|
||
> **SPEC-010 AMENDMENT §10.2.1 (REVISED):** EPOCH STATE RETENTION:
|
||
> Validators MUST retain the validator set (public keys) of the
|
||
> immediately previous epoch until the end of the current epoch. This
|
||
> means at any given time, a validator stores exactly two validator
|
||
> sets: the current epoch's and the previous epoch's. Upon transitioning
|
||
> to epoch N+1, the validator set for epoch N-1 is discarded.
|
||
|
||
**6.4 Test Assertions**
|
||
|
||
23. Assert that a certificate from the current epoch is accepted.
|
||
|
||
24. Assert that a certificate from epoch N-1 is accepted during epoch N
|
||
(grace period).
|
||
|
||
25. Assert that a certificate from epoch N-2 is rejected with
|
||
ERR_CERTIFICATE_EXPIRED during epoch N.
|
||
|
||
26. Assert that a certificate from epoch N+1 is rejected with
|
||
ERR_FUTURE_EPOCH during epoch N.
|
||
|
||
27. Assert that votes are verified against the CORRECT epoch's validator
|
||
set, not the current epoch's set, when the certificate's epoch
|
||
differs from the current epoch.
|
||
|
||
28. Assert that a certificate assembled with votes from a validator that
|
||
was present in epoch N but removed in epoch N+1 is still accepted
|
||
during epoch N+1 (grace period) because the votes are valid for
|
||
epoch N.
|
||
|
||
**AV-007: Stealth Address Unlinkability Breach via Ephemeral Key Reuse**
|
||
|
||
**Severity: S2 (HIGH)**
|
||
|
||
**7.1 Threat Description**
|
||
|
||
If a sender reuses an ephemeral key r when constructing stealth
|
||
addresses for two different payments to the same recipient, the
|
||
resulting stealth addresses P_stealth_1 and P_stealth_2 will be computed
|
||
from the same shared secret S, making them linkable. An observer who
|
||
sees the same ephemeral public key R in two different transaction memos
|
||
can conclude that both payments went to the same recipient.
|
||
|
||
More subtly, even if the ephemeral key is not reused, patterns in the
|
||
memo field (identical encrypted payload sizes, timing correlations, or
|
||
metadata leakage) could allow statistical linkability analysis.
|
||
|
||
**7.2 Root Cause**
|
||
|
||
SPEC-007 §7.4.1 step 1 states "Sender generates an ephemeral key pair:
|
||
(r, R = r·G)" but does not explicitly mandate that r MUST be fresh
|
||
randomness for every payment. The specification assumes correct
|
||
implementation but does not defend against implementation errors or
|
||
deliberate shortcuts.
|
||
|
||
**7.3 Mitigation**
|
||
|
||
> **SPEC-007 AMENDMENT §7.4.1 Step 1 (REVISED):** Sender generates an
|
||
> ephemeral key pair: r = CSPRNG(32 bytes), R = r·G. The value r MUST be
|
||
> generated from a cryptographically secure random number generator for
|
||
> EVERY payment. The value r MUST NOT be derived deterministically from
|
||
> the payment amount, recipient, or any other predictable input. After
|
||
> the Fulfill is received (or the payment expires), the value r MUST be
|
||
> securely erased from memory using zeroize. An implementation MUST NOT
|
||
> cache, reuse, or persist ephemeral keys across payments.
|
||
>
|
||
> **SPEC-007 AMENDMENT §7.4.2 (NEW):** EPHEMERAL KEY UNIQUENESS
|
||
> ENFORCEMENT: The sender's wallet MUST maintain a Bloom filter (or
|
||
> equivalent probabilistic set) of recently used ephemeral public keys
|
||
> R. Before using a new R, the wallet MUST check that R is not in the
|
||
> filter. If a collision is detected (which should only occur due to a
|
||
> CSPRNG failure), the wallet MUST generate a new r and retry. The Bloom
|
||
> filter SHOULD retain entries for at least 10,000 recent payments. This
|
||
> provides defence-in-depth against CSPRNG failures.
|
||
>
|
||
> **SPEC-007 AMENDMENT §7.4.3 (NEW):** MEMO PADDING: The encrypted memo
|
||
> field MUST be padded to a fixed length before encryption, regardless
|
||
> of the actual payload size. The padded length MUST be one of: 64, 128,
|
||
> 256, 512, 1024, or 2048 bytes (the smallest size that fits the
|
||
> payload). This prevents an observer from correlating transactions by
|
||
> memo ciphertext length. Padding MUST use random bytes (not
|
||
> zero-padding) to prevent distinguishing padding from payload without
|
||
> the decryption key.
|
||
|
||
**7.4 Test Assertions**
|
||
|
||
29. Assert that two payments to the same recipient produce different
|
||
stealth addresses (P_stealth_1 ≠ P_stealth_2).
|
||
|
||
30. Assert that two payments to the same recipient produce different
|
||
ephemeral public keys (R_1 ≠ R_2).
|
||
|
||
31. Assert that an observer with access to all SettlementCertificates
|
||
but without any view_key cannot determine if two stealth addresses
|
||
belong to the same recipient, with advantage ≤ negligible (formal:
|
||
advantage \< 2⁻¹²⁸).
|
||
|
||
32. Assert that the Bloom filter detects a deliberately reused ephemeral
|
||
key and forces regeneration.
|
||
|
||
33. Assert that the encrypted memo is padded to a fixed size: a 10-byte
|
||
payload and a 100-byte payload to the same recipient produce memos
|
||
of the same ciphertext length.
|
||
|
||
34. Assert that the ephemeral secret r is zeroed from memory after use
|
||
(test via memory inspection in debug mode).
|
||
|
||
**Summary of Specification Amendments**
|
||
|
||
------------ -------------- ---------------------- -------------------------------
|
||
**Attack** **Severity** **Amended Spec** **Key Change**
|
||
|
||
AV-001 S2 SPEC-008 §8.4.5, Maximum expiry decay per hop,
|
||
§8.5.1, §8.5.2 locking fee, sender reputation
|
||
scoring.
|
||
|
||
AV-002 S1 SPEC-002 §2.4.3 Rules Explicit enumeration of all
|
||
6--7 commitments requiring range
|
||
proofs, including fee.
|
||
Zero-value fee requires proof.
|
||
|
||
AV-003 S1 SPEC-002 §2.4.3 Rules Certificate store lookup
|
||
8--8a mandatory; new relevance check
|
||
prevents referencing unrelated
|
||
certificates.
|
||
|
||
AV-004 S1\* SPEC-003 §3.6, §3.6.1 Sharding invariant made
|
||
explicit as safety-critical.
|
||
Cross-shard credit via
|
||
certificate verification.
|
||
|
||
AV-005 S3 SPEC-002 §2.7.1, Rounding rule: floor +
|
||
SPEC-008 §8.2.1 remainder to sender.
|
||
min_destination_amount field in
|
||
Prepare packets.
|
||
|
||
AV-006 S1 SPEC-003 §3.3.2, One-epoch grace period for
|
||
SPEC-010 §10.2.1 certificates. Strict rejection
|
||
of certificates more than one
|
||
epoch old.
|
||
|
||
AV-007 S2 SPEC-007 §7.4.1--7.4.3 Mandatory fresh CSPRNG
|
||
ephemeral key, Bloom filter
|
||
uniqueness check, random-padded
|
||
memo to fixed lengths.
|
||
------------ -------------- ---------------------- -------------------------------
|
||
|
||
*\* AV-004 is mitigated by design if the specification is followed. The
|
||
severity rating reflects the consequence of non-conformant
|
||
implementation, not a flaw in the protocol.*
|
||
|
||
**Formal Verification Additions**
|
||
|
||
The following properties MUST be added to the formal verification
|
||
requirements in SPEC-010 §10.6:
|
||
|
||
------------------------- ----------------- ----------------------------
|
||
**Property** **Tool** **Scope**
|
||
|
||
Fee commitment range TLA+ Verify that no state is
|
||
(AV-002) reachable where a transition
|
||
with negative fee commitment
|
||
passes all validation rules.
|
||
|
||
Causal dependency TLA+ Verify that no state is
|
||
integrity (AV-003) reachable where an account
|
||
spends value credited by an
|
||
uncertified or irrelevant
|
||
transition.
|
||
|
||
Sharding safety (AV-004) TLA+ Verify that no state is
|
||
reachable where two
|
||
conflicting transitions for
|
||
the same account are
|
||
certified by different
|
||
shards.
|
||
|
||
Epoch-boundary TLA+ Verify that a certificate
|
||
certificate validity from epoch N-2 cannot be
|
||
(AV-006) applied in epoch N under any
|
||
message ordering.
|
||
|
||
Stealth address ProVerif Verify the PRIVACY-2
|
||
unlinkability (AV-007) property: an observer
|
||
without the view_key has
|
||
negligible advantage in
|
||
linking two stealth
|
||
addresses to the same
|
||
recipient.
|
||
------------------------- ----------------- ----------------------------
|
||
|
||
**Document Control**
|
||
|
||
------------- ------------ --------------- -------------------------------
|
||
**Version** **Date** **Author** **Description**
|
||
|
||
0.1.1 2026-03-11 MESH Foundation Security addendum. Mitigations
|
||
for 7 identified attack vectors
|
||
(AV-001 through AV-007).
|
||
------------- ------------ --------------- -------------------------------
|