reencode gossipsub messages with anonymization (#378)

This helps protect against clients sending more data than they should
and thus getting penalized on topics that require anonymity
This commit is contained in:
Jacek Sieka 2020-09-25 18:39:34 +02:00 committed by GitHub
parent 17e00e642a
commit 8ecef46738
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 21 additions and 16 deletions

View File

@ -83,7 +83,7 @@ proc send*(p: PubSub, peer: PubSubPeer, msg: RPCMsg) =
## ##
trace "sending pubsub message to peer", peer, msg = shortLog(msg) trace "sending pubsub message to peer", peer, msg = shortLog(msg)
peer.send(msg) peer.send(msg, p.anonymize)
proc broadcast*( proc broadcast*(
p: PubSub, p: PubSub,

View File

@ -193,7 +193,7 @@ proc sendImpl(conn: Connection, encoded: seq[byte]) {.async.} =
await conn.close() # This will clean up the send connection await conn.close() # This will clean up the send connection
proc send*(p: PubSubPeer, msg: RPCMsg) = proc send*(p: PubSubPeer, msg: RPCMsg, anonymize: bool) =
doAssert(not isNil(p), "pubsubpeer nil!") doAssert(not isNil(p), "pubsubpeer nil!")
let conn = p.sendConn let conn = p.sendConn
@ -203,15 +203,20 @@ proc send*(p: PubSubPeer, msg: RPCMsg) =
trace "sending msg to peer", peer = p, rpcMsg = shortLog(msg) trace "sending msg to peer", peer = p, rpcMsg = shortLog(msg)
# When sending messages, we take care to re-encode them with the right
# anonymization flag to ensure that we're not penalized for sending invalid
# or malicious data on the wire - in particular, re-encoding protects against
# some forms of valid but redundantly encoded protobufs with unknown or
# duplicated fields
let encoded = if p.hasObservers(): let encoded = if p.hasObservers():
# trigger send hooks # trigger send hooks
var mm = msg # hooks can modify the message var mm = msg # hooks can modify the message
p.sendObservers(mm) p.sendObservers(mm)
encodeRpcMsg(mm) encodeRpcMsg(mm, anonymize)
else: else:
# If there are no send hooks, we redundantly re-encode the message to # If there are no send hooks, we redundantly re-encode the message to
# protobuf for every peer - this could easily be improved! # protobuf for every peer - this could easily be improved!
encodeRpcMsg(msg) encodeRpcMsg(msg, anonymize)
if encoded.len <= 0: if encoded.len <= 0:
debug "empty message, skipping", p, msg debug "empty message, skipping", p, msg

View File

@ -31,7 +31,7 @@ func defaultMsgIdProvider*(m: Message): string =
byteutils.toHex(m.seqno) & $m.fromPeer byteutils.toHex(m.seqno) & $m.fromPeer
proc sign*(msg: Message, privateKey: PrivateKey): CryptoResult[seq[byte]] = proc sign*(msg: Message, privateKey: PrivateKey): CryptoResult[seq[byte]] =
ok((? privateKey.sign(PubSubPrefix & encodeMessage(msg))).getBytes()) ok((? privateKey.sign(PubSubPrefix & encodeMessage(msg, false))).getBytes())
proc verify*(m: Message): bool = proc verify*(m: Message): bool =
if m.signature.len > 0 and m.key.len > 0: if m.signature.len > 0 and m.key.len > 0:
@ -43,7 +43,7 @@ proc verify*(m: Message): bool =
var key: PublicKey var key: PublicKey
if remote.init(m.signature) and key.init(m.key): if remote.init(m.signature) and key.init(m.key):
trace "verifying signature", remoteSignature = remote trace "verifying signature", remoteSignature = remote
result = remote.verify(PubSubPrefix & encodeMessage(msg), key) result = remote.verify(PubSubPrefix & encodeMessage(msg, false), key)
if result: if result:
libp2p_pubsub_sig_verify_success.inc() libp2p_pubsub_sig_verify_success.inc()

View File

@ -78,24 +78,24 @@ proc write*(pb: var ProtoBuffer, field: int, subs: SubOpts) =
ipb.finish() ipb.finish()
pb.write(field, ipb) pb.write(field, ipb)
proc encodeMessage*(msg: Message): seq[byte] = proc encodeMessage*(msg: Message, anonymize: bool): seq[byte] =
var pb = initProtoBuffer() var pb = initProtoBuffer()
if len(msg.fromPeer) > 0: if len(msg.fromPeer) > 0 and not anonymize:
pb.write(1, msg.fromPeer) pb.write(1, msg.fromPeer)
pb.write(2, msg.data) pb.write(2, msg.data)
if len(msg.seqno) > 0: if len(msg.seqno) > 0 and not anonymize:
pb.write(3, msg.seqno) pb.write(3, msg.seqno)
for topic in msg.topicIDs: for topic in msg.topicIDs:
pb.write(4, topic) pb.write(4, topic)
if len(msg.signature) > 0: if len(msg.signature) > 0 and not anonymize:
pb.write(5, msg.signature) pb.write(5, msg.signature)
if len(msg.key) > 0: if len(msg.key) > 0 and not anonymize:
pb.write(6, msg.key) pb.write(6, msg.key)
pb.finish() pb.finish()
pb.buffer pb.buffer
proc write*(pb: var ProtoBuffer, field: int, msg: Message) = proc write*(pb: var ProtoBuffer, field: int, msg: Message, anonymize: bool) =
pb.write(field, encodeMessage(msg)) pb.write(field, encodeMessage(msg, anonymize))
proc decodeGraft*(pb: ProtoBuffer): ProtoResult[ControlGraft] {. proc decodeGraft*(pb: ProtoBuffer): ProtoResult[ControlGraft] {.
inline.} = inline.} =
@ -242,13 +242,13 @@ proc decodeMessages*(pb: ProtoBuffer): ProtoResult[seq[Message]] {.inline.} =
trace "decodeMessages: no messages found" trace "decodeMessages: no messages found"
ok(msgs) ok(msgs)
proc encodeRpcMsg*(msg: RPCMsg): seq[byte] = proc encodeRpcMsg*(msg: RPCMsg, anonymize: bool): seq[byte] =
trace "encodeRpcMsg: encoding message", msg = msg.shortLog() trace "encodeRpcMsg: encoding message", msg = msg.shortLog()
var pb = initProtoBuffer() var pb = initProtoBuffer()
for item in msg.subscriptions: for item in msg.subscriptions:
pb.write(1, item) pb.write(1, item)
for item in msg.messages: for item in msg.messages:
pb.write(2, item) pb.write(2, item, anonymize)
if msg.control.isSome(): if msg.control.isSome():
pb.write(3, msg.control.get()) pb.write(3, msg.control.get())
if len(pb.buffer) > 0: if len(pb.buffer) > 0: