From 3bec7385956bd00391e0e10c0c671d8d82653c8d Mon Sep 17 00:00:00 2001 From: HelloZeroNet Date: Tue, 20 Jan 2015 02:47:00 +0100 Subject: [PATCH] version 0.1.4, WIF compatible new private keys, proper bitcoin address verification, worker killing does not drops hash error, private key saved confirmation on site create --- src/Config.py | 4 +- src/Crypt/CryptBitcoin.py | 11 +- src/Site/SiteManager.py | 2 +- src/Worker/Worker.py | 3 + src/lib/BitcoinECC/newBitcoinECC.py | 460 ++++++++++++++++++++++++++++ src/main.py | 13 +- 6 files changed, 481 insertions(+), 12 deletions(-) create mode 100644 src/lib/BitcoinECC/newBitcoinECC.py diff --git a/src/Config.py b/src/Config.py index c7e92cee..f44c3758 100644 --- a/src/Config.py +++ b/src/Config.py @@ -3,7 +3,7 @@ import ConfigParser class Config(object): def __init__(self): - self.version = "0.1.3" + self.version = "0.1.4" self.parser = self.createArguments() argv = sys.argv[:] # Copy command line arguments argv = self.parseConfig(argv) # Add arguments from config file @@ -47,7 +47,7 @@ class Config(object): action.add_argument('peer_port', help='Peer port to publish (default: random peer port from tracker)', default=15441, nargs='?') # SiteVerify - action = subparsers.add_parser("siteVerify", help='Verify site files using md5: address') + action = subparsers.add_parser("siteVerify", help='Verify site files using sha512: address') action.add_argument('address', help='Site to verify') diff --git a/src/Crypt/CryptBitcoin.py b/src/Crypt/CryptBitcoin.py index f7ba209f..63199ffe 100644 --- a/src/Crypt/CryptBitcoin.py +++ b/src/Crypt/CryptBitcoin.py @@ -1,11 +1,12 @@ from src.lib.BitcoinECC import BitcoinECC -import hashlib -def newPrivatekey(): # Return new private key - bitcoin = BitcoinECC.Bitcoin() - bitcoin.GeneratePrivateKey() - return bitcoin.PrivateEncoding() +def newPrivatekey(uncompressed=True): # Return new private key + from src.lib.BitcoinECC import newBitcoinECC # Use new lib to generate WIF compatible addresses, but keep using the old yet for backward compatiblility issues + bitcoin = newBitcoinECC.Bitcoin() + d = bitcoin.GenerateD() + bitcoin.AddressFromD(d, uncompressed) + return bitcoin.PrivFromD(d, uncompressed) def privatekeyToAddress(privatekey): # Return address from private key diff --git a/src/Site/SiteManager.py b/src/Site/SiteManager.py index daf5479d..80d5a8e9 100644 --- a/src/Site/SiteManager.py +++ b/src/Site/SiteManager.py @@ -37,7 +37,7 @@ def load(): # Checks if its a valid address def isAddress(address): - return re.match("^[A-Za-z0-9]{34}$", address) + return re.match("^[A-Za-z0-9]{26,35}$", address) # Return site and start download site files diff --git a/src/Worker/Worker.py b/src/Worker/Worker.py index b648f0da..7bb5ea66 100644 --- a/src/Worker/Worker.py +++ b/src/Worker/Worker.py @@ -32,6 +32,9 @@ class Worker: self.task = task task["workers_num"] += 1 buff = self.peer.getFile(task["site"].address, task["inner_path"]) + if self.running == False: # Worker no longer needed or got killed + self.manager.log.debug("%s: No longer needed, returning: %s" % (self.key, task["inner_path"])) + return None if buff: # Download ok correct = task["site"].verifyFile(task["inner_path"], buff) else: # Download error diff --git a/src/lib/BitcoinECC/newBitcoinECC.py b/src/lib/BitcoinECC/newBitcoinECC.py new file mode 100644 index 00000000..b09386bc --- /dev/null +++ b/src/lib/BitcoinECC/newBitcoinECC.py @@ -0,0 +1,460 @@ +import random +import hashlib +import base64 + +class GaussInt: + def __init__(self,x,y,n,p=0): + if p: + self.x=x%p + self.y=y%p + self.n=n%p + else: + self.x=x + self.y=y + self.n=n + + self.p=p + + def __add__(self,b): + return GaussInt(self.x+b.x,self.y+b.y,self.n,self.p) + + def __sub__(self,b): + return GaussInt(self.x-b.x,self.y-b.y,self.n,self.p) + + def __mul__(self,b): + return GaussInt(self.x*b.x+self.n*self.y*b.y,self.x*b.y+self.y*b.x,self.n,self.p) + + def __div__(self,b): + return GaussInt((self.x*b.x-self.n*self.y*b.y)/(b.x*b.x-self.n*b.y*b.y),(-self.x*b.y+self.y*b.x)/(b.x*b.x-self.n*b.y*b.y),self.n,self.p) + + def __eq__(self,b): + return self.x==b.x and self.y==b.y + + def __repr__(self): + if self.p: + return "%s+%s (%d,%d)"%(self.x,self.y,self.n,self.p) + else: + return "%s+%s (%d)"%(self.x,self.y,self.n) + + def __pow__(self,n): + b=Base(n,2) + t=GaussInt(1,0,self.n) + while b: + t=t*t + if b.pop(): + t=self*t + + return t + + def Inv(self): + return GaussInt(self.x/(self.x*self.x-self.n*self.y*self.y),-self.y/(self.x*self.x-self.n*self.y*self.y),self.n,self.p) + + def Eval(self): + return self.x.Eval()+self.y.Eval()*math.sqrt(self.n) + +def Cipolla(a,p): + b=0 + while pow((b*b-a)%p,(p-1)/2,p)==1: + b+=1 + + return (GaussInt(b,1,b**2-a,p)**((p+1)/2)).x + +def InvMod(a,n): + m=[] + + s=n + while n: + m.append(a/n) + (a,n)=(n,a%n) + + u=1 + v=0 + while m: + (u,v)=(v,u-m.pop()*v) + + return u%s + +def Base(n,b): + l=[] + while n: + l.append(n%b) + n/=b + + return l + +def MsgMagic(message): + return "\x18Bitcoin Signed Message:\n"+chr(len(message))+message + +def Hash(m,method): + h=hashlib.new(method) + h.update(m) + + return h.digest() + +def b58encode(v): + #Encode a byte string to the Base58 + digit="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + base=len(digit) + val=0 + for c in v: + val*=256 + val+=ord(c) + + result="" + while val: + (val,mod)=divmod(val,base) + result=digit[mod]+result + + pad=0 + for c in v: + if c=="\x00": + pad+=1 + else: + break + + return (digit[0]*pad)+result + +def b58decode(v): + #Decode a Base58 string to byte string + digit="123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" + base=len(digit) + val=0 + for c in v: + val*=base + val+=digit.find(c) + + result="" + while val: + (val,mod)=divmod(val,256) + result=chr(mod)+result + + pad=0 + for c in v: + if c==digit[0]: + pad+=1 + else: + break + + return "\x00"*pad+result + +def Byte2Int(b): + n=0 + for x in b: + n*=256 + n+=ord(x) + + return n + +def Byte2Hex(b): + #Convert a byte string to hex number + out="" + for x in b: + y=hex(ord(x))[2:] + if len(y)==1: + y="0"+y + out+="%2s"%y + + return out + +def Int2Byte(n,b): + #Convert a integer to a byte string of length b + out="" + + for _ in range(b): + (n,m)=divmod(n,256) + out=chr(m)+out + + return out + +class EllipticCurvePoint: + #Main class + #It's a point on an Elliptic Curve + + def __init__(self,x,a,b,p,n=0): + #We store the coordinate in x and the elliptic curve parameter. + #x is of length 3. This is the 3 projective coordinates of the point. + self.x=x[:] + self.a=a + self.b=b + self.p=p + self.n=n + + def __add__(self,y): + #The main function to add self and y + #It uses the formulas I derived in projective coordinates. + #Projectives coordinates are more efficient than the usual (x,y) coordinates + #because we don't need to compute inverse mod p, which is faster. + z=EllipticCurvePoint([0,0,0],self.a,self.b,self.p) + + if self==y: + d=(2*self.x[1]*self.x[2])%self.p + d3=pow(d,3,self.p) + n=(3*pow(self.x[0],2,self.p)+self.a*pow(self.x[2],2,self.p))%self.p + + z.x[0]=(pow(n,2,self.p)*d*self.x[2]-2*d3*self.x[0])%self.p + z.x[1]=(3*self.x[0]*n*pow(d,2,self.p)-pow(n,3,self.p)*self.x[2]-self.x[1]*d3)%self.p + z.x[2]=(self.x[2]*d3)%self.p + else: + d=(y.x[0]*self.x[2]-y.x[2]*self.x[0])%self.p + d3=pow(d,3,self.p) + n=(y.x[1]*self.x[2]-self.x[1]*y.x[2])%self.p + + z.x[0]=(y.x[2]*self.x[2]*pow(n,2,self.p)*d-d3*(y.x[2]*self.x[0]+y.x[0]*self.x[2]))%self.p + z.x[1]=(pow(d,2,self.p)*n*(2*self.x[0]*y.x[2]+y.x[0]*self.x[2])-pow(n,3,self.p)*self.x[2]*y.x[2]-self.x[1]*d3*y.x[2])%self.p + z.x[2]=(self.x[2]*d3*y.x[2])%self.p + + return z + + def __mul__(self,n): + #The fast multiplication of point n times by itself. + b=Base(n,2) + t=EllipticCurvePoint(self.x,self.a,self.b,self.p) + b.pop() + while b: + t+=t + if b.pop(): + t+=self + + return t + + def __repr__(self): + #print a point in (x,y) coordinate. + return "x=%d\ny=%d\n"%((self.x[0]*InvMod(self.x[2],self.p))%self.p,(self.x[1]*InvMod(self.x[2],self.p))%self.p) + + def __eq__(self,y): + #Does self==y ? + #It computes self cross product with x and check if the result is 0. + return self.x[0]*y.x[1]==self.x[1]*y.x[0] and self.x[1]*y.x[2]==self.x[2]*y.x[1] and self.x[2]*y.x[0]==self.x[0]*y.x[2] and self.a==y.a and self.b==y.b and self.p==y.p + + def __ne__(self,y): + #Does self!=x ? + return not (self == y) + + def Normalize(self): + #Transform projective coordinates of self to the usual (x,y) coordinates. + if self.x[2]: + self.x[0]=(self.x[0]*InvMod(self.x[2],self.p))%self.p + self.x[1]=(self.x[1]*InvMod(self.x[2],self.p))%self.p + self.x[2]=1 + elif self.x[1]: + self.x[0]=(self.x[0]*InvMod(self.x[1],self.p))%self.p + self.x[1]=1 + elif self.x[0]: + self.x[0]=1 + else: + raise Exception + + def Check(self): + #Is self on the curve ? + return (self.x[0]**3+self.a*self.x[0]*self.x[2]**2+self.b*self.x[2]**3-self.x[1]**2*self.x[2])%self.p==0 + + + def CryptAddr(self,filename,password,Address): + txt="" + for tag in Address: + (addr,priv)=Address[tag] + if priv: + txt+="%s\t%s\t%s\n"%(tag,addr,priv) + else: + txt+="%s\t%s\t\n"%(tag,addr) + + txt+="\x00"*(15-(len(txt)-1)%16) + + password+="\x00"*(15-(len(password)-1)%16) + crypt=twofish.Twofish(password).encrypt(txt) + + f=open(filename,"wb") + f.write(crypt) + f.close() + + def GenerateD(self): + #Generate a private key. It's just a random number between 1 and n-1. + #Of course, this function isn't cryptographically secure. + #Don't use it to generate your key. Use a cryptographically secure source of randomness instead. + #return random.randint(1,self.n-1) + return random.SystemRandom().randint(1,self.n-1) # Better random fix + + def CheckECDSA(self,sig,message,Q): + #Check a signature (r,s) of the message m using the public key self.Q + # and the generator which is self. + #This is not the one used by Bitcoin because the public key isn't known; + # only a hash of the public key is known. See the function VerifyMessageFromAddress. + (r,s)=sig + + if Q.x[2]==0: + return False + if not Q.Check(): + return False + if (Q*self.n).x[2]!=0: + return False + if r<1 or r>self.n-1 or s<1 or s>self.n-1: + return False + + z=Byte2Int(Hash(Hash(MsgMagic(message),"SHA256"),"SHA256")) + + w=InvMod(s,self.n) + u1=(z*w)%self.n + u2=(r*w)%self.n + R=self*u1+Q*u2 + R.Normalize() + + return (R.x[0]-r)%self.n==0 + + def SignMessage(self,message,priv): + #Sign a message. The private key is self.d. + (d,uncompressed)=self.DFromPriv(priv) + + z=Byte2Int(Hash(Hash(MsgMagic(message),"SHA256"),"SHA256")) + + r=0 + s=0 + while not r or not s: + #k=random.randint(1,self.n-1) + k=random.SystemRandom().randint(1,self.n-1) # Better random fix + R=self*k + R.Normalize() + r=R.x[0]%self.n + s=(InvMod(k,self.n)*(z+r*d))%self.n + + val=27 + if not uncompressed: + val+=4 + + return base64.standard_b64encode(chr(val)+Int2Byte(r,32)+Int2Byte(s,32)) + + def VerifyMessageFromAddress(self,addr,message,sig): + #Check a signature (r,s) for the message m signed by the Bitcoin + # address "addr". + + sign=base64.standard_b64decode(sig) + (r,s)=(Byte2Int(sign[1:33]),Byte2Int(sign[33:65])) + + z=Byte2Int(Hash(Hash(MsgMagic(message),"SHA256"),"SHA256")) + + val=ord(sign[0]) + if val<27 or val>=35: + return False + + if val>=31: + uncompressed=False + val-=4 + else: + uncompressed=True + + x=r + y2=(pow(x,3,self.p) + self.a*x + self.b) % self.p + y=Cipolla(y2,self.p) + + for _ in range(2): + kG=EllipticCurvePoint([x,y,1],self.a,self.b,self.p,self.n) + mzG=self*((-z)%self.n) + Q=(kG*s+mzG)*InvMod(r,self.n) + + if self.AddressFromPublicKey(Q,uncompressed)==addr: + return True + + y=self.p-y + + return False + + def AddressFromPrivate(self,priv): + #Transform a private key to a bitcoin address. + (d,uncompressed)=self.DFromPriv(priv) + + return self.AddressFromD(d,uncompressed) + + def PrivFromD(self,d,uncompressed): + #Encode a private key self.d to base58 encoding. + p=Int2Byte(d,32) + p="\x80"+p + + if not uncompressed: + p+=chr(1) + + cs=Hash(Hash(p,"SHA256"),"SHA256")[:4] + + return b58encode(p+cs) + + def DFromPriv(self,priv): + uncompressed=(len(priv)==51) + priv=b58decode(priv) + + if uncompressed: + priv=priv[:-4] + else: + priv=priv[:-5] + + return (Byte2Int(priv[1:]),uncompressed) + + def AddressFromPublicKey(self,Q,uncompressed): + #Find the bitcoin address from the public key self.Q + #We do normalization to go from the projective coordinates to the usual + # (x,y) coordinates. + Q.Normalize() + if uncompressed: + pk=chr(4)+Int2Byte(Q.x[0],32)+Int2Byte(Q.x[1],32) + else: + pk=chr(2+Q.x[1]%2)+Int2Byte(Q.x[0],32) + + kh=chr(0)+Hash(Hash(pk,"SHA256"),"RIPEMD160") + cs=Hash(Hash(kh,"SHA256"),"SHA256")[:4] + + return b58encode(kh+cs) + + def AddressFromD(self,d,uncompressed): + #Computes a bitcoin address given the private key self.d. + return self.AddressFromPublicKey(self*d,uncompressed) + + def IsValid(self,addr): + adr=b58decode(addr) + kh=adr[:-4] + cs=adr[-4:] + + verif=Hash(Hash(kh,"SHA256"),"SHA256")[:4] + + return cs==verif + + def AddressGenerator(self,k,uncompressed=True): + #Generate Bitcoin address and write them in the multibit format. + #Change the date as you like. + liste={} + for i in range(k): + d=self.GenerateD() + addr=self.AddressFromD(d,uncompressed) + priv=self.PrivFromD(d,uncompressed) + liste[i]=[addr,priv] + print "%s %s"%(addr, priv) + + return liste + +def Bitcoin(): + a=0 + b=7 + p=2**256-2**32-2**9-2**8-2**7-2**6-2**4-1 + Gx=int("79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798",16) + Gy=int("483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8",16) + n=int("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141",16) + + return EllipticCurvePoint([Gx,Gy,1],a,b,p,n) + +def main(): + bitcoin=Bitcoin() + + #Generate an adress from the private key + privkey = "PrivatekeyinBase58" + adr = bitcoin.AddressFromPrivate(privkey) + print "Address : ", adr + + #Sign a message with the current address + m="Hello World" + sig=bitcoin.SignMessage("Hello World", privkey) + #Verify the message using only the bitcoin adress, the signature and the message. + #Not using the public key as it is not needed. + if bitcoin.VerifyMessageFromAddress(adr,m,sig): + print "Message verified" + + #Generate some addresses + print "Here are some adresses and associated private keys" + bitcoin.AddressGenerator(10) + +if __name__ == "__main__": main() diff --git a/src/main.py b/src/main.py index cc324e08..46d576a7 100644 --- a/src/main.py +++ b/src/main.py @@ -73,11 +73,16 @@ def siteCreate(): logging.info("Generating new privatekey...") from src.Crypt import CryptBitcoin privatekey = CryptBitcoin.newPrivatekey() - logging.info("-----------------------------------------------------------") - logging.info("Site private key: %s (save it, required to modify the site)" % privatekey) + logging.info("----------------------------------------------------------------------") + logging.info("Site private key: %s" % privatekey) + logging.info(" !!! ^ Save it now, required to modify the site ^ !!!") address = CryptBitcoin.privatekeyToAddress(privatekey) - logging.info("Site address: %s" % address) - logging.info("-----------------------------------------------------------") + logging.info("Site address: %s" % address) + logging.info("----------------------------------------------------------------------") + + while True: + if raw_input("? Have you secured your private key? (yes, no) > ").lower() == "yes": break + else: logging.info("Please, secure it now, you going to need it to modify your site!") logging.info("Creating directory structure...") from Site import Site