From dad054d6b357b1925271f17b7badcd1df91dc105 Mon Sep 17 00:00:00 2001 From: Alvaro Revuelta Date: Fri, 28 Jun 2024 09:25:10 +0200 Subject: [PATCH] fix(rln): nullifierlog vulnerability (#2855) --- waku/waku_rln_relay/rln_relay.nim | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/waku/waku_rln_relay/rln_relay.nim b/waku/waku_rln_relay/rln_relay.nim index ab4ba10a6a..85890a2cd2 100644 --- a/waku/waku_rln_relay/rln_relay.nim +++ b/waku/waku_rln_relay/rln_relay.nim @@ -144,7 +144,7 @@ proc updateLog*( try: # check if an identical record exists if rlnPeer.nullifierLog[epoch].hasKeyOrPut(proofMetadata.nullifier, proofMetadata): - # the above condition could be `discarded` but it is kept for clarity, that slashing will + # the above condition could be `discarded` but it is kept for clarity, that slashing will # be implemented here # TODO: slashing logic return ok() @@ -279,8 +279,10 @@ proc validateMessageAndUpdateLog*( if proofMetadataRes.isErr(): return MessageValidationResult.Invalid - # insert the message to the log (never errors) - discard rlnPeer.updateLog(msgProof.epoch, proofMetadataRes.get()) + # insert the message to the log (never errors) only if the + # message is valid. + if isValidMessage == MessageValidationResult.Valid: + discard rlnPeer.updateLog(msgProof.epoch, proofMetadataRes.get()) return isValidMessage @@ -308,22 +310,25 @@ proc appendRLNProof*( let proof = rlnPeer.groupManager.generateProof(input, epoch, nonce).valueOr: return err("could not generate rln-v2 proof: " & $error) - msg.proof = proof.encode().buffer return ok() proc clearNullifierLog*(rlnPeer: WakuRlnRelay) = # clear the first MaxEpochGap epochs of the nullifer log # if more than MaxEpochGap epochs are in the log - # note: the epochs are ordered ascendingly - if rlnPeer.nullifierLog.len().uint <= rlnPeer.rlnMaxEpochGap: - return - - let countToClear = rlnPeer.nullifierLog.len().uint - rlnPeer.rlnMaxEpochGap - trace "clearing epochs from the nullifier log", count = countToClear - let epochsToClear = rlnPeer.nullifierLog.keys().toSeq()[0 ..< countToClear] - for epoch in epochsToClear: - rlnPeer.nullifierLog.del(epoch) + let currentEpoch = fromEpoch(rlnPeer.getCurrentEpoch()) + + var epochsToRemove: seq[Epoch] = @[] + for epoch in rlnPeer.nullifierLog.keys(): + let epochInt = fromEpoch(epoch) + + # clean all epochs that are +- rlnMaxEpochGap from the current epoch + if (currentEpoch+rlnPeer.rlnMaxEpochGap) <= epochInt or epochInt <= (currentEpoch-rlnPeer.rlnMaxEpochGap): + epochsToRemove.add(epoch) + + for epochRemove in epochsToRemove: + trace "clearing epochs from the nullifier log", currentEpoch = currentEpoch, cleanedEpoch = fromEpoch(epochRemove) + rlnPeer.nullifierLog.del(epochRemove) proc generateRlnValidator*( wakuRlnRelay: WakuRLNRelay, spamHandler = none(SpamHandler)