import random
import hashlib
import base64
import math

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()
