diff --git a/client/electrum b/client/electrum index 85822684..c16b8ae2 100755 --- a/client/electrum +++ b/client/electrum @@ -16,7 +16,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import re,sys +import re, sys, getpass from optparse import OptionParser @@ -30,7 +30,7 @@ urldecode = lambda x: _ud.sub(lambda m: chr(int(m.group(1), 16)), x) if __name__ == '__main__': - known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import'] + known_commands = ['help', 'validateaddress', 'balance', 'contacts', 'create', 'payto', 'sendtx', 'password', 'newaddress', 'addresses', 'history', 'label', 'gui', 'mktx','seed','import','signmessage','verifymessage'] usage = "usage: %prog [options] command args\nCommands: "+ (', '.join(known_commands)) @@ -65,20 +65,21 @@ if __name__ == '__main__': params = o[1].split('&') else: params = [] - cmd = 'gui' + amount = label = signature = signer = '' for p in params: k,v = p.split('=') - v = urldecode(v) - if k=='amount': amount = v - elif k=='label': label = v - elif k =='signature': signature = v - elif k =='signer': signer = v + uv = urldecode(v) + if k=='amount': amount = uv + elif k=='label': label = uv + elif k =='signature': signature = uv + elif k =='signer': signer = uv else: print k,v - + if k in ['signer','signature']: + cmd = cmd.replace('&%s=%s'%(k,v),'') + if signature: - message = p.replace('signer=%s'%signer,'').replace('signature=%s'%signature,'') - wallet.verify_message(signer, signature,message) + wallet.verify_message(signer, signature, cmd ) gui.set_send_tab(address, amount, label) @@ -153,7 +154,7 @@ if __name__ == '__main__': wallet.save() # commands needing password - if cmd in ['payto', 'password', 'mktx', 'seed', 'import' ] or ( cmd=='addresses' and options.show_keys): + if cmd in ['payto', 'password', 'mktx', 'seed', 'import','signmessage' ] or ( cmd=='addresses' and options.show_keys): password = getpass.getpass('Password:') if wallet.use_encryption else None # check password try: @@ -334,3 +335,11 @@ if __name__ == '__main__': else: print "error: mismatch" + elif cmd == 'signmessage': + address, message = args[1:3] + print wallet.sign_message(address, message, password) + + elif cmd == 'verifymessage': + address, signature, message = args[1:4] + print wallet.verify_message(address, signature, message) + diff --git a/client/wallet.py b/client/wallet.py index ada85a8b..eb8abc58 100644 --- a/client/wallet.py +++ b/client/wallet.py @@ -17,8 +17,7 @@ # along with this program. If not, see . -import sys, base64, os, re, hashlib, socket, getpass, copy, operator, ast, random -from decimal import Decimal +import sys, base64, os, re, hashlib, copy, operator, ast try: import ecdsa @@ -151,7 +150,6 @@ def int_to_hex(i, length=1): return s.decode('hex')[::-1].encode('hex') - # AES EncodeAES = lambda secret, s: base64.b64encode(aes.encryptData(secret,s)) DecodeAES = lambda secret, e: aes.decryptData(secret, base64.b64decode(e)) @@ -343,11 +341,73 @@ class Wallet: pk = number_to_string(secexp,order) return pk + + def sign_message(self, address, message, password): + private_key = ecdsa.SigningKey.from_string( self.get_private_key2(address, password), curve = SECP256k1 ) + public_key = private_key.get_verifying_key() + signature = private_key.sign_digest( Hash( message ), sigencode = ecdsa.util.sigencode_string ) + assert public_key.verify_digest( signature, Hash( message ), sigdecode = ecdsa.util.sigdecode_string) + for i in range(4): + sig = base64.b64encode( chr(27+i) + signature ) + if self.verify_message( address, sig, message): + return sig + else: + raise BaseException("error: cannot sign message") + - - def verify_message(self, signing_address, signature, message): - """ recover public key from signature; """ - pass + def verify_message(self, address, signature, message): + """ See http://www.secg.org/download/aid-780/sec1-v2.pdf for the math """ + + from ecdsa import numbertheory, ellipticcurve, util + import msqr + curve = curve_secp256k1 + G = generator_secp256k1 + order = G.order() + + sig = base64.b64decode(signature) + if len(sig) != 65: raise BaseException("error") + recid = ord(sig[0]) - 27 + # print "recid", recid + + # extract r,s from signature + r,s = util.sigdecode_string(sig[1:], order) + + # 1.1 + x = r + (recid/2) * order + + # 1.3 + alpha = ( x * x * x + curve.a() * x + curve.b() ) % curve.p() + beta = msqr.modular_sqrt(alpha, curve.p()) + y = beta if (beta - recid) % 2 == 0 else curve.p() - beta + + # 1.4 the constructor checks that nR is at infinity + try: + R = ellipticcurve.Point(curve, x, y, order) + except: + print "not in curve" + return False + + # 1.5 compute e from message: + h = Hash(message) + e = string_to_number(h) + minus_e = -e % order + + # 1.6 compute Q = r^-1 (sR - eG) + inv_r = numbertheory.inverse_mod(r,order) + Q = inv_r * ( s * R + minus_e * G ) + public_key = ecdsa.VerifyingKey.from_public_point( Q, curve = SECP256k1 ) + + # check that Q is the public key + try: + public_key.verify_digest( sig[1:], h, sigdecode = ecdsa.util.sigdecode_string) + except: + print "wrong key" + return False + + # check that we get the original signing address + addr = public_key_to_bc_address( '04'.decode('hex') + public_key.to_string() ) + # print addr + return address == addr def create_new_address2(self, for_change):