Skip to content

Commit 8854f68

Browse files
committed
Merge #1070: Validate message signature encoding
0507f6a Validate message signature encoding (Adam Gibson) Pull request description: Fixes #1069. Before this commit, a non-hex encoded pubkey sent as part of the privmsg signature raised an Exception, this commit fixes this, checking the encoding of both the pubkey and signature fields before sending them from daemon to client. ACKs for top commit: kristapsk: ACK 0507f6a. Checked diff between commits, test suite passes, did successful signet coinjoin. Merging. Tree-SHA512: d6b59fc340b015157544330a0a4624999559d9f2bcc5ee1ef189558bdab5fb2a2cf80287d1020bfe5be1f4919037b794a0fa4bbe2292a80aca0a03e9de23384a
2 parents 647fa6d + 0507f6a commit 8854f68

File tree

2 files changed

+29
-11
lines changed

2 files changed

+29
-11
lines changed

jmdaemon/jmdaemon/message_channel.py

+21-6
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
encrypted_commands, commitment_broadcast_list, offername_list,\
99
fidelity_bond_cmd_list
1010
from jmbase.support import get_log
11+
from jmbase import hextobin
1112
from functools import wraps
1213

1314
log = get_log()
@@ -918,24 +919,38 @@ def on_privmsg(self, nick, message):
918919
#Other ill formatted messages will be caught in the try block.
919920
if len(message) < 2:
920921
return
921-
922922
if message[0] != COMMAND_PREFIX:
923923
log.debug('message not a cmd')
924924
return
925925
cmd_string = message[1:].split(' ')[0]
926926
if cmd_string not in plaintext_commands + encrypted_commands:
927927
log.debug('cmd not in cmd_list, line="' + message + '"')
928928
return
929+
badsigmsg = "Sig not properly appended to privmsg, ignoring"
929930
#Verify nick ownership
930-
sig = message[1:].split(' ')[-2:]
931+
try:
932+
pub, sig = message[1:].split(' ')[-2:]
933+
except Exception:
934+
log.debug(badsigmsg)
935+
return
931936
#reconstruct original message without cmd
932937
rawmessage = ' '.join(message[1:].split(' ')[1:-2])
933-
#sanity check that the sig was appended properly
934-
if len(sig) != 2 or len(rawmessage) == 0:
935-
log.debug("Sig not properly appended to privmsg, ignoring")
938+
# can happen if not enough fields for command, (stuff), pub, sig:
939+
if len(rawmessage) == 0:
940+
log.debug(badsigmsg)
941+
return
942+
# Sanitising signature before attempting to verify:
943+
# Note that the sig itself can be any garbage, because `ecdsa_verify`
944+
# swallows any fail and returns False; but the pubkey is assumed
945+
# to be hex-encoded, and the signature base64 encoded, so check early:
946+
try:
947+
dummypub = hextobin(pub)
948+
dummysig = base64.b64decode(sig)
949+
except Exception:
950+
log.debug(badsigmsg)
936951
return
937952
self.daemon.request_signature_verify(
938-
rawmessage + str(self.hostid), message, sig[1], sig[0], nick,
953+
rawmessage + str(self.hostid), message, sig, pub, nick,
939954
NICK_HASH_LENGTH, NICK_MAX_ENCODED, str(self.hostid))
940955

941956
def on_verified_privmsg(self, nick, message):

jmdaemon/test/test_message_channel.py

+8-5
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717
import threading
1818
import binascii
1919
import jmbitcoin as bitcoin
20+
from jmbase import bintohex
2021
from dummy_mc import DummyMessageChannel
2122

23+
# note: completely invalid sig is fine, as long as it's encoded right:
24+
dummy_sig_str = bintohex(b"\x02"*33) + " " + base64.b64encode(b"\x01"*72).decode()
2225

2326
jlog = get_log()
2427

@@ -151,8 +154,8 @@ def test_setup_mc():
151154
#Simulate order receipt on 2 of 3 msgchans from this nick;
152155
#note that it will have its active chan set to mc "1" because that
153156
#is the last it was seen on:
154-
dmcs[0].on_privmsg(cp1, "!reloffer 0 4000 5000 100 0.2 abc def")
155-
dmcs[1].on_privmsg(cp1, "!reloffer 0 4000 5000 100 0.2 abc def")
157+
dmcs[0].on_privmsg(cp1, "!reloffer 0 4000 5000 100 0.2 " + dummy_sig_str)
158+
dmcs[1].on_privmsg(cp1, "!reloffer 0 4000 5000 100 0.2 " + dummy_sig_str)
156159
time.sleep(0.5)
157160
#send back a response
158161
mcc.privmsg(cp1, "fill", "0")
@@ -210,7 +213,7 @@ def test_setup_mc():
210213
#first, pretend they all showed up on all 3 mcs:
211214
for m in dmcs:
212215
for cp in cps:
213-
m.on_privmsg(cp, "!reloffer 0 400000 500000 100 0.002 abc def")
216+
m.on_privmsg(cp, "!reloffer 0 400000 500000 100 0.002 " + dummy_sig_str)
214217
#next, call main fill function
215218
mcc.fill_orders(new_offers, 1000, "dummypubkey", "dummycommit")
216219
#now send a dummy transaction to this same set.
@@ -242,7 +245,7 @@ def test_setup_mc():
242245
#have the cps rearrive
243246
for m in dmcs:
244247
for cp in cps:
245-
m.on_privmsg(cp, "!reloffer 0 4000 5000 100 0.2 abc def")
248+
m.on_privmsg(cp, "!reloffer 0 4000 5000 100 0.2 " + dummy_sig_str)
246249

247250
#####################################################################
248251
#next series of messages are to test various normal and abnormal
@@ -255,7 +258,7 @@ def test_setup_mc():
255258
#invalid missing field
256259
dmcs[0].on_pubmsg(cps[2], "!hp2")
257260
#receive commitment via privmsg to trigger commitment_transferred
258-
dmcs[0].on_privmsg(cps[2], "!hp2 deadbeef abc def")
261+
dmcs[0].on_privmsg(cps[2], "!hp2 deadbeef " + dummy_sig_str)
259262
#simulate receipt of order cancellation
260263
#valid
261264
dmcs[0].on_pubmsg(cps[2], "!cancel 2")

0 commit comments

Comments
 (0)