From a590699d92b4006f0be991ba3928f071fa519bf9 Mon Sep 17 00:00:00 2001 From: thomasv Date: Mon, 6 Feb 2012 17:59:31 +0100 Subject: [PATCH] introducing signed aliases --- client/gui.py | 47 ++++++++++++++++++++++++++----------- client/wallet.py | 60 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 74 insertions(+), 33 deletions(-) diff --git a/client/gui.py b/client/gui.py index 397c0b477..8182dfc8d 100644 --- a/client/gui.py +++ b/client/gui.py @@ -588,7 +588,7 @@ class BitcoinGUI: r = r.strip() if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): try: - to_address = self.wallet.get_alias(r) + to_address = self.wallet.read_alias(r)[0] except: continue if to_address: @@ -807,22 +807,43 @@ class BitcoinGUI: entry.set_text('') + def question(self,msg): + dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg) + dialog.show() + result = dialog.run() + dialog.destroy() + return result == gtk.RESPONSE_OK + def get_alias(self, r): try: - to_address = self.wallet.get_alias(r) + target, signing_address, auth_name = self.wallet.read_alias(r) except BaseException, e: - to_address = None - msg = "Warning: the key corresponding to %s does not match its previously known value.\nDo you wish to accept the new key?"%r - dialog = gtk.MessageDialog( self.window, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_QUESTION, gtk.BUTTONS_OK_CANCEL, msg) - dialog.show() - result = dialog.run() - dialog.destroy() - if result != gtk.RESPONSE_CANCEL: - print e.message - to_address = e.message - self.wallet.aliases[r] = to_address + # raise exception if verify fails (verify the chain) + self.show_message("Alias error: " + e.message) + return - return to_address + if auth_name is None: + a = self.wallet.aliases.get(r) + if not a: + if self.question( "Warning: the alias is self-signed. Do you want to trust address %s ?"%to_address ): + self.wallet.aliases[r] = signing_address + else: + target = None + else: + if signing_address != a: + if self.question( "Warning: the signing key of %s does not match its previously known value! It is possible that someone is trying to do something nasty!!!\nDo you wish to accept the new key?"%r ): + self.wallet.aliases[r] = signing_address + else: + target = None + else: + if signing_address not in self.wallet.authorities.keys(): + if self.question( "Warning: the alias '%s' was signed by %s [%s].\n\nDo you want to add this key to your list of trusted keys?"\ + %(r,auth_name,signing_address)): + self.wallet.authorities[signing_address] = auth_name + else: + target = None + + return target diff --git a/client/wallet.py b/client/wallet.py index c0759adb6..7b68e725c 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -248,6 +248,8 @@ class Wallet: self.history = {} self.labels = {} # labels for addresses and transactions self.aliases = {} # aliases for addresses + self.authorities = {} # trusted addresses + self.receipts = {} # signed URIs self.receipt = None # next receipt self.addressbook = [] # outgoing addresses, for payments @@ -383,7 +385,7 @@ class Wallet: order = G.order() # extract r,s from signature sig = base64.b64decode(signature) - if len(sig) != 65: raise BaseException("error") + if len(sig) != 65: raise BaseException("Wrong encoding") r,s = util.sigdecode_string(sig[1:], order) recid = ord(sig[0]) - 27 # 1.1 @@ -407,7 +409,10 @@ class Wallet: # check that we get the original signing address addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) # print addr - assert address == addr + try: + assert address == addr + except: + raise BaseException("Bad signature") def create_new_address2(self, for_change): @@ -489,6 +494,7 @@ class Wallet: 'contacts':self.addressbook, 'imported_keys':self.imported_keys, 'aliases':self.aliases, + 'authorities':self.authorities, 'receipts':self.receipts, } f = open(self.path,"w") @@ -521,6 +527,7 @@ class Wallet: self.addressbook = d.get('contacts') self.imported_keys = d.get('imported_keys',{}) self.aliases = d.get('aliases',{}) + self.authorities = d.get('authorities',{}) self.receipts = d.get('receipts',{}) except: raise BaseException(upgrade_msg) @@ -724,34 +731,47 @@ class Wallet: self.receipt = None return True, out - def get_alias(self, x): + + def read_alias(self, alias): # this might not be the right place for this function. import urllib - if self.is_valid(x): - return x + if self.is_valid(alias): + return alias else: - m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', x) - m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', x) + m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias) + m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias) if m1: url = 'http://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) elif m2: - url = 'http://' + x + '/bitcoin.id' + url = 'http://' + alias + '/bitcoin.id' else: return '' try: - print url - xx = urllib.urlopen(url).read().strip() + lines = urllib.urlopen(url).readlines() except: return '' - if not self.is_valid(xx): - return '' - self.labels[xx] = x - s = self.aliases.get(x) - if s: - if s != xx: - raise BaseException( xx ) + # line 0 + line = lines[0].strip().split(':') + if len(line) == 1: + auth_name = None + target = signing_addr = line[0] else: - self.aliases[x] = xx - - return xx + target, auth_name, signing_addr, signature = line + msg = "alias:%s:%s:%s"%(alias,target,auth_name) + print msg, signature + self.verify_message(signing_addr, signature, msg) + + # other lines are signed updates + for line in lines[1:]: + line = line.strip() + if not line: continue + line = line.split(':') + previous = target + print repr(line) + target, signature = line + self.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) + + assert self.is_valid(target) + + return target, signing_addr, auth_name