Skip to content
This repository has been archived by the owner on May 13, 2022. It is now read-only.

use maker input pubkey instead of coinjoin pubkey for authorization #90

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions encryption_protocol.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,12 @@ transactions and not to Maker and Taker entities.
Encrypted
=========

TAK: !auth <input utxo pubkey> <btc sig of taker encryption pubkey using input utxo pubkey>
TAK: !auth <input utxo pubkey1[, input utxo pubkey2,..,M]> <btc sig of taker encryption pubkey using input utxo pubkey>
(Maker verifies the btc sig; if not valid, connection is dropped - send REJECT message)
MAK: !ioauth <utxo list> <coinjoin pubkey> <change address> <btc sig of maker encryption pubkey using coinjoin pubkey>
If the input utxo pubkey field is a comma-separated list instead of a single value, it is interpreted as the data
for a multisig address, with the last field being the M in M of N (and N being the number of pubkeys. The btc sig
can then be using any of the given pubkeys.)
MAK: !ioauth <utxo list> <input pubkey> <coinjoin address> <change address> <btc sig of maker encryption pubkey using input pubkey>
(Taker verifies the btc sig; if not valid, as for previous)

Because the !auth messages are under encryption, there is no privacy leak of bitcoin pubkeys or output addresses.
Expand Down
16 changes: 16 additions & 0 deletions lib/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,22 @@ def get_network():
'''Returns network name'''
return config.get("BLOCKCHAIN","network")

def get_multisig_addr(pubkeys, M, N):
'''Note on usage: It is critical to remember
that the multisig address constructed DOES depend
on the order of the pubkeys! An attempt to sign off
a transaction must have the order of signatures correct.'''
mscript = btc.mk_multisig_script(pubkeys, M, N).decode('hex')
return btc.script_to_address(mscript, get_magic_byte())

def get_magic_byte():
if get_addr_vbyte() == 0x00:
return 5
elif get_addr_vbyte() == 0x6f:
return 196
else:
raise Exception("Unrecognized network type")

def get_addr_vbyte():
if get_network() == 'testnet':
return 0x6f
Expand Down
20 changes: 11 additions & 9 deletions lib/irc.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,9 +129,9 @@ def cancel_orders(self, oid_list):
def send_pubkey(self, nick, pubkey):
self.__privmsg(nick, 'pubkey', pubkey)

def send_ioauth(self, nick, utxo_list, cj_pubkey, change_addr, sig):
authmsg = (str(','.join(utxo_list)) + ' ' +
cj_pubkey + ' ' + change_addr + ' ' + sig)
def send_ioauth(self, nick, utxo_list, input_pubkey, cj_addr, change_addr, sig):
authmsg = (str(','.join(utxo_list)) + ' ' + input_pubkey + ' ' +
cj_addr + ' ' + change_addr + ' ' + sig)
self.__privmsg(nick, 'ioauth', authmsg)

def send_sigs(self, nick, sig_list):
Expand Down Expand Up @@ -212,11 +212,13 @@ def __on_privmsg(self, nick, message):
self.on_pubkey(nick, maker_pk)
elif chunks[0] == 'ioauth':
utxo_list = chunks[1].split(',')
cj_pub = chunks[2]
change_addr = chunks[3]
btc_sig = chunks[4]
input_pubkey = chunks[2]
cj_addr = chunks[3]
change_addr = chunks[4]
btc_sig = chunks[5]
if self.on_ioauth:
self.on_ioauth(nick, utxo_list, cj_pub, change_addr, btc_sig)
self.on_ioauth(nick, utxo_list,
input_pubkey, cj_addr, change_addr, btc_sig)
elif chunks[0] == 'sig':
sig = chunks[1]
if self.on_sig:
Expand All @@ -234,12 +236,12 @@ def __on_privmsg(self, nick, message):
self.on_order_fill(nick, oid, amount, taker_pk)
elif chunks[0] == 'auth':
try:
i_utxo_pubkey = chunks[1]
pubkeys = chunks[1].split(',')
btc_sig = chunks[2]
except (ValueError, IndexError) as e:
self.send_error(nick, str(e))
if self.on_seen_auth:
self.on_seen_auth(nick, i_utxo_pubkey, btc_sig)
self.on_seen_auth(nick, pubkeys, btc_sig)
elif chunks[0] == 'tx':
b64tx = chunks[1]
try:
Expand Down
49 changes: 38 additions & 11 deletions lib/maker.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,20 +56,39 @@ def __init__(self, maker, nick, oid, amount, taker_pk):
# orders to find out which addresses you use
self.maker.msgchan.send_pubkey(nick, self.kp.hex_pk())

def auth_counterparty(self, nick, i_utxo_pubkey, btc_sig):
self.i_utxo_pubkey = i_utxo_pubkey

if not btc.ecdsa_verify(self.taker_pk, btc_sig, self.i_utxo_pubkey):
def auth_counterparty(self, nick, pubkeys, btc_sig):
if len(pubkeys)>1:
try:
M = int(pubkeys[-1])
except ValueError as e:
debug("Failed to parse multisig keys, "+e)
return False
if M not in range(1,15):
debug("Invalid M for M of N multisig: "+str(M))
return False
if len(pubkeys) not in range(3,17):
debug("Invalid number of pubkeys for multisig: "+str(M))
return False
self.i_utxo_pubkeys = pubkeys
pk_verified = False
for i,p in enumerate(self.i_utxo_pubkeys):
if i > 0 and i==len(self.i_utxo_pubkeys)-1: continue
if btc.ecdsa_verify(self.taker_pk, btc_sig, p):
pk_verified = True
break
if not pk_verified:
print 'signature didnt match pubkey and message'
return False
#authorisation of taker passed
#(but input utxo pubkey is checked in verify_unsigned_tx).
#Send auth request to taker
#TODO the next 2 lines are a little inefficient.
btc_key = self.maker.wallet.get_key_from_addr(self.cj_addr)
auth_utxo = self.utxos[sorted(self.utxos.keys())[0]]
btc_key = self.maker.wallet.get_key_from_addr(auth_utxo['address'])
btc_pub = btc.privtopub(btc_key)
btc_sig = btc.ecdsa_sign(self.kp.hex_pk(), btc_key)
self.maker.msgchan.send_ioauth(nick, self.utxos.keys(), btc_pub, self.change_addr, btc_sig)
self.maker.msgchan.send_ioauth(nick, self.utxos.keys(),
btc_pub, self.cj_addr, self.change_addr, btc_sig)
return True

def recv_tx(self, nick, txhex):
Expand Down Expand Up @@ -128,9 +147,17 @@ def verify_unsigned_tx(self, txd):
if None in input_utxo_data:
return False, 'some utxos already spent or not confirmed yet'
input_addresses = [u['address'] for u in input_utxo_data]
if btc.pubtoaddr(self.i_utxo_pubkey, get_addr_vbyte())\
not in input_addresses:
return False, "authenticating bitcoin address is not contained"
if len(self.i_utxo_pubkeys) == 1:
if btc.pubtoaddr(self.i_utxo_pubkeys[0], get_addr_vbyte())\
not in input_addresses:
return False, "authenticating bitcoin address is not contained"
else:
M = self.i_utxo_pubkeys[-1]
N = len(self.i_utxo_pubkeys)-1
claimed_msig_addr = get_multisig_addr(self.i_utxo_pubkeys[:-1], M, N)
if claimed_msig_addr not in input_addresses:
return False, "authenticating bitcoin address is not contained"

my_utxo_set = set(self.utxos.keys())
if not tx_utxo_set.issuperset(my_utxo_set):
return False, 'my utxos are not contained'
Expand Down Expand Up @@ -193,10 +220,10 @@ def on_order_fill(self, nick, oid, amount, taker_pubkey):
finally:
self.wallet_unspent_lock.release()

def on_seen_auth(self, nick, pubkey, sig):
def on_seen_auth(self, nick, pubkeys, sig):
if nick not in self.active_orders or self.active_orders[nick] == None:
self.msgchan.send_error(nick, 'No open order from this nick')
self.active_orders[nick].auth_counterparty(nick, pubkey, sig)
self.active_orders[nick].auth_counterparty(nick, pubkeys, sig)
#TODO if auth_counterparty returns false, remove this order from active_orders
# and send an error

Expand Down
2 changes: 1 addition & 1 deletion lib/message_channel.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,5 +75,5 @@ def register_maker_callbacks(self, on_orderbook_requested=None, on_order_fill=No
def announce_orders(self, orderlist, nick=None): pass #nick=None means announce publicly
def cancel_orders(self, oid_list): pass
def send_pubkey(self, nick, pubkey): pass
def send_ioauth(self, nick, utxo_list, cj_pubkey, change_addr, sig): pass
def send_ioauth(self, nick, utxo_list, input_pubkey, cj_addr, change_addr, sig): pass
def send_sigs(self, nick, sig_list): pass
23 changes: 15 additions & 8 deletions lib/taker.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,24 @@ def start_encryption(self, nick, maker_pk):
my_btc_sig = btc.ecdsa_sign(self.kp.hex_pk(), my_btc_priv)
self.msgchan.send_auth(nick, my_btc_pub, my_btc_sig)

def auth_counterparty(self, nick, btc_sig, cj_pub):
def auth_counterparty(self, nick, btc_sig, input_pubkey, utxo_list):
'''Validate the counterpartys claim to own the btc
address/pubkey that will be used for coinjoining
with an ecdsa verification.'''
if not btc.ecdsa_verify(self.crypto_boxes[nick][0], btc_sig, cj_pub):
with an ecdsa verification. The btc pubkey used is
that corresponding to the first utxo in the provided
utxo list.'''
if not btc.ecdsa_verify(self.crypto_boxes[nick][0], btc_sig, input_pubkey):
print 'signature didnt match pubkey and message'
return False
#check that the pubkey used was indeed that for an input
#specified in the utxos
input_utxo_data = common.bc_interface.query_utxo_set(list(utxo_list))
input_addresses = [u['address'] for u in input_utxo_data]
if btc.pubtoaddr(input_pubkey, get_addr_vbyte()) not in input_addresses:
return False, "authenticating bitcoin address is not contained"
return True

def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
def recv_txio(self, nick, utxo_list, cj_addr, change_addr):
if nick not in self.nonrespondants:
debug('nick(' + nick + ') not in nonrespondants ' + str(self.nonrespondants))
return
Expand All @@ -77,7 +85,6 @@ def recv_txio(self, nick, utxo_list, cj_pub, change_addr):
total_input - self.cj_amount - order['txfee'] + real_cjfee})
print 'fee breakdown for %s totalin=%d cjamount=%d txfee=%d realcjfee=%d' % (nick,
total_input, self.cj_amount, order['txfee'], real_cjfee)
cj_addr = btc.pubtoaddr(cj_pub, get_addr_vbyte())
self.outputs.append({'address': cj_addr, 'value': self.cj_amount})
self.cjfee_total += real_cjfee
self.nonrespondants.remove(nick)
Expand Down Expand Up @@ -259,12 +266,12 @@ def on_error(self):
def on_pubkey(self, nick, maker_pubkey):
self.cjtx.start_encryption(nick, maker_pubkey)

def on_ioauth(self, nick, utxo_list, cj_pub, change_addr, btc_sig):
if not self.cjtx.auth_counterparty(nick, btc_sig, cj_pub):
def on_ioauth(self, nick, utxo_list, input_pubkey, cj_addr, change_addr, btc_sig):
if not self.cjtx.auth_counterparty(nick, btc_sig, input_pubkey, utxo_list):
print 'Authenticated encryption with counterparty: ' + nick + \
' not established. TODO: send rejection message'
return
self.cjtx.recv_txio(nick, utxo_list, cj_pub, change_addr)
self.cjtx.recv_txio(nick, utxo_list, cj_addr, change_addr)

def on_sig(self, nick, sig):
self.cjtx.add_signature(sig)
Expand Down