# Copyright (c) 2014, The Monero Project
# 
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without modification, are
# permitted provided that the following conditions are met:
# 
# 1. Redistributions of source code must retain the above copyright notice, this list of
#    conditions and the following disclaimer.
# 
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
#    of conditions and the following disclaimer in the documentation and/or other
#    materials provided with the distribution.
# 
# 3. Neither the name of the copyright holder nor the names of its contributors may be
#    used to endorse or promote products derived from this software without specific
#    prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
# THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

import hashlib
import struct
import base64
import binascii
import sys
from Crypto.Util import number
import Crypto.Random.random as rand
import Keccak
from collections import namedtuple
import copy

KEK=Keccak.Keccak(1600)
CURVE_P = (2**255 - 19)
b = 256
q = 2**255 - 19
l = 2**252 + 27742317777372353535851937790883648493
BASEPOINT = "0900000000000000000000000000000000000000000000000000000000000000"

#####################################
#Bernstein(?) Eddie Library in python
#####################################

def H(m):
    return hashlib.sha512(m).digest()

def expmod(b,e,m):
    if e == 0: return 1
    t = expmod(b,e/2,m)**2 % m
    if e & 1: t = (t*b) % m
    return t

def inv(x):
    return expmod(x,q-2,q)

d = -121665 * inv(121666)
I = expmod(2,(q-1)/4,q)

def xrecover(y):
    xx = (y*y-1) * inv(d*y*y+1)
    x = expmod(xx,(q+3)/8,q)
    if (x*x - xx) % q != 0: x = (x*I) % q
    if x % 2 != 0: x = q-x
    return x

By = 4 * inv(5)
Bx = xrecover(By)
B = [Bx % q,By % q]

def edwards(P,Q):
    x1 = P[0]
    y1 = P[1]
    x2 = Q[0]
    y2 = Q[1]
    x3 = (x1*y2+x2*y1) * inv(1+d*x1*x2*y1*y2)
    y3 = (y1*y2+x1*x2) * inv(1-d*x1*x2*y1*y2)
    return [x3 % q,y3 % q]

def scalarmult(P, e):
    if e == 0: return [0,1]
    Q = scalarmult(P,e/2)
    Q = edwards(Q,Q)
    if e & 1: Q = edwards(Q,P)
    return Q

def encodeint(y):
    bits = [(y >> i) & 1 for i in range(b)]
    return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)])

def encodepoint(P):
    x = P[0]
    y = P[1]
    bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1]
    return ''.join([chr(sum([bits[i * 8 + j] << j for j in range(8)])) for i in range(b/8)])

def bit(h,i):
    return (ord(h[i/8]) >> (i%8)) & 1

def public_key(sk):
    A = scalarmult(B,sk)
    return encodepoint(A)


def Hint(m):
    h = H(m)
    return sum(2**i * bit(h,i) for i in range(2*b))

def signature(m,sk,pk):
    h = H(sk)
    a = 2**(b-2) + sum(2**i * bit(h,i) for i in range(3,b-2))
    r = Hint(''.join([h[i] for i in range(b/8,b/4)]) + m)
    R = scalarmult(B,r)
    S = (r + Hint(encodepoint(R) + pk + m) * a) % l
    return encodepoint(R) + encodeint(S)

def isoncurve(P):
    x = P[0]
    y = P[1]
    return (-x*x + y*y - 1 - d*x*x*y*y) % q == 0

def decodeint(s):
    return sum(2**i * bit(s,i) for i in range(0,b))

def decodepoint(s):
    y = sum(2**i * bit(s,i) for i in range(0,b-1))
    x = xrecover(y)
    if x & 1 != bit(s,b-1): x = q-x
    P = [x,y]
    if not isoncurve(P): raise Exception("decoding point that is not on curve")
    return P

def checkvalid(s,m,pk):
    if len(s) != b/4: raise Exception("signature length is wrong")
    if len(pk) != b/8: raise Exception("public-key length is wrong")
    R = decodepoint(s[0:b/8])
    A = decodepoint(pk)
    S = decodeint(s[b/8:b/4])
    h = Hint(encodepoint(R) + pk + m)
    if scalarmult(B,S) != edwards(R,scalarmult(A,h)):
        raise Exception("signature does not pass verification")

#################################
#curve stuff, 
#mostly from https://github.com/monero-project/bitmonero/blob/1b8a68f6c1abcf481652c2cfd87300a128e3eb32/src/crypto/crypto-ops.c
#partial reference for fe things https://godoc.org/github.com/agl/ed25519/edwards25519
#note ge is the edwards version of the curve
#fe is the monty version of the curve
#################################


#NOT USED IN MININERO - Use ge_scalarmult_base
def ge_fromfe_frombytesvartime(s):
    #inputs something s (I assume in bytes)
    #inputs into montgomery form (fe)
    #then, turns it into edwards form (ge)
    #then r is the edwards curve point r->
    #reference 1: http://crypto.stackexchange.com/questions/9536/converting-ed25519-public-key-to-a-curve25519-public-key?rq=1
    #reference 2: https://github.com/orlp/ed25519/blob/master/src/key_exchange.c
    #best reference https://www.imperialviolet.org/2013/12/25/elligator.html
    
    #the point of this function is to return a ge_p2 from an int s
    #whereas, the similar function ge_frombytes_vartime returns a gep3
    return

def ge_double_scalarmult_base_vartime(aa, AA, bb):
    #a very nice comment in the CN code for this one!
    #r = a * A + b * B
    #where a = a[0]+256*a[1]+...+256^31 a[31].
    #and b = b[0]+256*b[1]+...+256^31 b[31].
    #B is the Ed25519 base point (x,4/5) with x positive.
    #cf also https://godoc.org/github.com/agl/ed25519/edwards25519
    tmpa = ge_scalarmult(aa, AA)
    tmpb = ge_scalarmult(bb, BASEPOINT)
    return toHex(edwards(toPoint(tmpa), toPoint(tmpb)))

def ge_double_scalarmult_vartime(aa, AA, bb, BB):
    #a very nice comment in the CN code for this one!
    #r = a * A + b * B
    #where a = a[0]+256*a[1]+...+256^31 a[31].
    #and b = b[0]+256*b[1]+...+256^31 b[31].
    #B is the Ed25519 base point (x,4/5) with x positive.
    #cf also https://godoc.org/github.com/agl/ed25519/edwards25519
    tmpa = ge_scalarmult(aa, AA)
    tmpb = ge_scalarmult(bb, BB)
    return toHex(edwards(toPoint(tmpa), toPoint(tmpb)))


def toPoint(pubkey):
    #turns hex key into x, y field coords
    return decodepoint(pubkey.decode("hex"))

def toHex(point):
    #turns point into pubkey (reverse of toPoint)
    return encodepoint(point).encode("hex")

def ge_scalarmult(a, A):
    #so I guess given any point A, and an integer a, this computes aA
    #so the seecond arguement is definitely an EC point
    # from http://cr.yp.to/highspeed/naclcrypto-20090310.pdf
    # "Alice's secret key a is a uniform random 32-byte string then 
    #clampC(a) is a uniform random Curve25519 secret key
    #i.e. n, where n/8 is a uniform random integer between 
    #2^251 and 2^252-1
    #Alice's public key is n/Q compressed to the x-coordinate
    #so that means, ge_scalarmult is not actually doing scalar mult
    #clamping makes the secret be between 2^251 and 2^252
    #and should really be done
    #print(toPoint(A))
    return encodepoint(scalarmult(toPoint(A), a)).encode("hex") # now using the eddie function

def ge_scalarmult_base(a):
    #in this function in the original code, they've assumed it's already clamped ... 
    #c.f. also https://godoc.org/github.com/agl/ed25519/edwards25519
    #it will return h = a*B, where B is ed25519 bp (x,4/5)
    #and a = a[0] + 256a[1] + ... + 256^31 a[31]
    #it assumes that a[31 <= 127 already
    return ge_scalarmult(8*a, BASEPOINT)

#NOT USED IN MININERO - use ge_scalarmult_base
def ge_frombytes_vartime(key):
    #https://www.imperialviolet.org/2013/12/25/elligator.html
    #basically it takes some bytes of data
    #converts to a point on the edwards curve
    #if the bytes aren't on the curve
    #also does some checking on the numbers
    #ex. your secret key has to be at least >=4294967277
    #also it rejects certain curve points, i.e. "if x = 0, sign must be positive
    return 0

#NOT USED IN MININERO - unecessary as all operations are from hex
def ge_p1p1_to_p2(p):
    #there are two ways of representing the points
    ##http://code.metager.de/source/xref/lib/nacl/20110221/crypto_sign/edwards25519sha512batch/ref/ge25519.c
    #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
    return

#NOT USED IN MININERO -unnecessary as operations are from hex
def ge_p2_dbl():
    #basically it doubles a point and doubles it
    #c.f. Explicit Formulas for Doubling (towards bottom)
    #Explicit formulas for doubling
    #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
    return

#NOT USED IN MININERO - unnecessary as operations are from hex
def ge_p3_to_p2():
    #basically, it copies a point in 3 coordinates to another point
    #c.f. Explicit Formulas for Doubling (towards bottom)
    #Explicit formulas for doubling
    #http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html
    return

def ge_mul8(P):
    #ok, the point of this is to double three times
    #and the point is that the ge_p2_dbl returns a point in the p1p1 form
    #so that's why have to convert it first and then double
    return ge_scalarmult(8, P)

def sc_reduce(s):
    #inputs a 64 byte int and outputs the lowest 32 bytes
    #used by hash_to_scalar, which turns cn_fast_hash to number..
    r = longToHex(s)
    r = r[64::] 
    #print("before mod p", r)
    return hexToLong(r) % CURVE_P

def sc_reduce32(data):
    #ok, the code here is exactly the same as sc_reduce
    #(which is default lib sodium)
    #except it is assumed that your input
    #s is alread in the form:
    # s[0]+256*s[1]+...+256^31*s[31] = s
    #and the rest is just reducing mod l
    #so basically take a 32 byte input, and reduce modulo the prime
    return data % CURVE_P 

def sc_mulsub(a, b, c):
    #takes in a, b, and c
    #This is used by the regular sig 
    #i.e. in generate_signature
    #returns c-ab mod l
    a = number.bytes_to_long(a[::-1])
    b = number.bytes_to_long(b[::-1])
    c = number.bytes_to_long(c[::-1])
    return (c - a * b) % CURVE_P


##########################################
#Hashing
#this is where keccak, H_p, and H_s come in..
######################################

def cn_fast_hash(key, size):
    #see ReadMeKeccak.txt
    return KEK.Keccak((size,key.encode("hex")),1088,512,256,False)


###################################################
#CryptoNote Things
#Mainly from https://github.com/monero-project/bitmonero/blob/1b8a68f6c1abcf481652c2cfd87300a128e3eb32/src/crypto/crypto.cpp
###################################################

def random_scalar():
    #Gets a random scalar mod q and 32 bytes
    tmp = rand.getrandbits(64 * 8) # 8 bits to a byte ...  
    tmp = sc_reduce(tmp) #-> turns 64 to 32 (note sure why don't just gt 32 in first place ... )
    return tmp

def hash_to_scalar(data, length):
    #this one is H_s(P)
    #relies on cn_fast_hash and sc_reduce32 (which makes an int smaller)
    #the input here is not necessarily a 64 byte thing, and that's why sc_reduce32 
    res = hexToLong(cn_fast_hash(data, length))
    return sc_reduce32(res)
    

def generate_keys():
    #should return a secret key and public key pair
    #once you have the secret key,
    #then the public key be gotten from 25519 function
    #so just need to generate random
    #first generate random 32-byte(256 bit) integer, copy to result
    #ok, just sc_reduce, what that does is takes 64 byte int, turns into 32 byte int...
    #so sc_reduce is legit and comes from another library http://hackage.haskell.org/package/ed25519-0.0.2.0/src/src/cbits/sc_reduce.c
    #as far as I can tell, sc
    #basically this gets you an int which is sufficiently large
    #import Crypto.Random.random as rand
    rng = random_scalar()
    #sec = hex(rng).rstrip("L").lstrip("0x") or "0"
    sec = sc_reduce32(rng)
    pub = public_key(sec).encode("hex")
    #pub = ge_scalarmult_base(sec)
    #print(rng.decode("hex"))
    #sec = curve25519_mult(rng, basepoint)

    #the point of ge_p3_tobytes here is just store as bytes...
    #and p3 is a way to store points on the ge curve
    return sec, pub

def check_key(key):
    #inputs a public key, and outputs if point is on the curve
    return isoncurve(toPoint(key))

def secret_key_to_public_key(secret_key):
    #turns a secret key to a public key
    #the actual function returns as bytes since they mult the fast way.
    if sc_check(secret_key) != 0:
        print "error in sc_check"
        quit()
    return public_key(secret_key)

def hash_to_ec(key):
    #takes a hash and turns into a point on the curve
    #In MININERO, I'm not using the byte representation
    #So this function is superfluous
    h = hash_to_scalar(key, len(key))
    point = ge_scalarmult_base(h)
    return ge_mul8(point)


def generate_key_image(public_key, secret_key):
    #should return a key image as defined in whitepaper
    if sc_check(secret_key) != 0:
        print"sc check error in key image"
    point = hash_to_ec(public_key)
    point2 = ge_scalarmult(secret_key, point)
    return point2 

def generate_ring_signature(prefix, image, pubs, pubs_count, sec, sec_index):
    #returns a ring signature
    #prefix is whatever message
    #image is key image I = xH(P)
    #pubs is [pub1, pub2, pub3]
    #pubs_count is number in pubs (ex 3)
    #sec is your secret key x
    #sec_index is which of the pubs is your pub
    if sec_index >= pubs_count:
        print "bad index of secret key!"
        quit()
    if ge_frombytes_vartime(image) != 0:
        print"bad image!"
        quit()
    summ = 0
    aba = [0 for xx in range(pubs_count)] 
    abb = [0 for xx in range(pubs_count)] 
    sigc = [0 for xx in range(pubs_count)] #these are the c[i]'s from the whitepaper
    sigr =[0 for xx in range(pubs_count)] #these are the r[i]'s from the whitepaper
    for ii in range(0, pubs_count):
        if (ii == sec_index):
            kk = random_scalar()
            tmp3 = ge_scalarmult_base(kk) #L[i] for i = s
            aba[ii] = tmp3
            tmp3 = hash_to_ec(pubs[ii]) #R[i] for i = s
            abb[ii] = ge_scalarmult(kk, tmp3) 
        else:
            k1 = random_scalar() #note this generates a random scalar in the correct range...
            k2 = random_scalar()
            if ge_frombytes_vartime(pubs[ii]) != 0:
                print "error in ring sig!!!"
                quit()
            tmp2 = ge_double_scalarmult_base_vartime(k1, pubs[ii], k2) #this is L[i] for i != s
            aba[ii] = tmp2
            tmp3 = hash_to_ec(pubs[ii])
            abb[ii] = ge_double_scalarmult_vartime(k2, tmp3, k1, image) #R[i] for i != s
            sigc[ii] = k1  #the random c[i] for i != s
            sigr[ii] = k2  #the random r[i] for i != s
            summ = sc_add(summ, sigc[ii]) #summing the c[i] to get the c[s] via page 9 whitepaper
    
    buf = struct.pack('64s', prefix)
    for ii in range(0, pubs_count):
        buf += struct.pack('64s', aba[ii])
        buf += struct.pack('64s', abb[ii])
    hh = hash_to_scalar(buf,len(buf))
    sigc[sec_index] = sc_sub(hh, summ) # c[s] = hash - sum c[i] mod l
    sigr[sec_index] = sc_mulsub(sigc[sec_index], sec, kk) # r[s] = q[s] - sec * c[index]
    return image, sigc, sigr


            
     


def check_ring_signature(prefix, key_image, pubs, pubs_count, sigr, sigc):
    #from https://github.com/monero-project/bitmonero/blob/6a70de32bf872d97f9eebc7564f1ee41ff149c36/src/crypto/crypto.cpp
    #this is the "ver" algorithm from CN
    #takes the prefix (message)
    #key_image is I= xH(P)
    #pubs is like [pub1, pub2, pub3]
    #pubs_count is length of above list (3 in this case)
    #sigr is  list of the r-values in the ring sig [r[0], r[1], ...]
    #sigc is a list of the c-values in the ring sig [c[0], c[1], ...]
    aba = [0 for xx in range(pubs_count)] 
    abb = [0 for xx in range(pubs_count)] 

    if ge_frombytes_vartime(key_image) != 0:
        print "ring image error in checking sigs"
        quit()
    summ = 0
    buf = struct.pack('64s', prefix)
    for ii in range(0, pubs_count):
        if ((sc_check(sigc[ii]) != 0) or (sc_check(sigr[ii]) != 0)):
            print "failed sc_check in check ring sigs"
            quit()
        if ge_frombytes_vartime(pubs[ii]) != 0:
            print "public key is a bad point in ring sigs"
            quit()

        tmp2 = ge_double_scalarmult_base_vartime(sigc[ii], pubs[ii], sigr[ii]) 
        aba[ii] = tmp2
        tmp3 = hash_to_ec(pubs[ii])
        tmp2 = ge_double_scalarmult_vartime(sigr[ii], tmp3, sigc[ii], key_image)
        abb[ii] = tmp2
        summ = sc_add(summ, sigc[ii])
    for ii in range(0, pubs_count):
        buf += struct.pack('64s', aba[ii])
        buf += struct.pack('64s', abb[ii])
    
    hh = hash_to_scalar(buf,len(buf))
    hh = sc_sub(hh, summ)
    return sc_isnonzero(hh) == 0

def generate_key_derivation(key1, key2):
    #key1 is public key of receiver Bob (see page 7)
    #key2 is Alice's private
    #this is a helper function for the key-derivation
    #which is the generating one-time key's thingy
    if sc_check(key2) != 0:
        #checks that the secret key is uniform enough... 
        print"error in sc_check in keyder"
        quit()
    if ge_frombytes_vartime(key1) != 0:
        print "didn't pass curve checks in keyder"
        quit()
    
    point = key1 ## this ones the public
    point2 = ge_scalarmult( key2, point)
    #print("p2", encodepoint(point2).encode("hex"))
    point3 = ge_mul8(point2) #This has to do with n==0 mod 8 by dedfinition, c.f. the top paragraph of page 5 of http://cr.yp.to/ecdh/curve25519-20060209.pdf
    #and also c.f. middle of page 8 in same document (Bernstein)
    return point3

def derivation_to_scalar(derivation, output_index):
    #this function specifically hashes your
    #output index (for the one time keys )
    #in order to get an int, so we can do ge_mult_scalar
    #buf = s_comm(d = derivation, o = output_index)
    buf2 = struct.pack('64sl', derivation, output_index) 
    #print(buf2)
    return hash_to_scalar(buf2, len(buf2))

def derive_public_key(derivation, output_index, base ):
    if ge_frombytes_vartime(base) != 0: #check some conditions on the point
        print"derive pub key bad point"
        quit()
    point1 = base
    scalar = derivation_to_scalar(derivation, output_index)
    point2 = ge_scalarmult_base(scalar)
    point3 = point2 #I think the cached is just for the sake of adding
    #because the CN code adds using the monty curve
    point4 = edwards(toPoint(point1), toPoint(point3))
    return point4

def sc_add(aa, bb):
    return (aa + bb ) %CURVE_P
def sc_sub(aa, bb):
    return (aa - bb ) %CURVE_P

def sc_isnonzero(c):
    return (c %CURVE_P != 0 )

def sc_mulsub(aa, bb, cc):
    return (cc - aa * bb ) %CURVE_P

def derive_secret_key(derivation, output_index, base):
    #outputs a derived key...
    if sc_check(base) !=0:
        print"cs_check in derive_secret_key"
    scalar = derivation_to_scalar(derivation, output_index)
    return base + scalar
    
class s_comm:
    #this is a helper struct in the original CN code
    #didn't actually end up using it here..
    #though originally I was doing so.
    def __init__(self, **kwds):
        self.__dict__.update(kwds)

def generate_signature(prefix_hash, pub, sec):
    #gets the "usual" signature (not ring sig)
    #buf = s_comm(h=prefix_hash, key=pub, comm=0) #see the pack below
    k = random_scalar()
    tmp3 = ge_scalarmult_base(k)
    buf2 = struct.pack('64s64s64s', prefix_hash, pub, tmp3) 
    sigc = hash_to_scalar(buf2, len(buf2))
    return sc_mulsub(sigc, sec, k), sigc

def check_signature(prefix_hash, pub, sigr, sigc):
    #checking the normal sigs, not the ring sigs...
    if ge_frombytes_vartime(pub) !=0:
        print "bad point, check sig!"
        quit() 
    if (sc_check(sigc) != 0) or (sc_check(sigr) != 0):
        print"sc checksig error!"
        quit() 
    tmp2 = ge_double_scalarmult_base_vartime(sigc, pub, sigr)
    buf2 = struct.pack('64s64s64s', prefix_hash, pub, tmp2) 
    c = hash_to_scalar(buf2, len(buf2))
    c = sc_sub(c, sigc)
    return sc_isnonzero(c) == 0

def hexToLong(a):
    return number.bytes_to_long(a.decode("hex"))

def longToHex(a):
    return number.long_to_bytes(a).encode("hex")

def hexToBits(a):
    return a.decode("hex")

def bitsToHex(a):
    return a.encode("hex")

def sc_check(key):
    #in other words, keys which are too small are rejected
    return 0 
    #s0, s1, s2, s3, s4, s5, s6, s7 = load_4(longToHex(key))
    #return (signum_(1559614444 - s0) + (signum_(1477600026 - s1) << 1) + (signum_(2734136534 - s2) << 2) + (signum_(350157278 - s3) << 3) + (signum_(-s4) << 4) + (signum_(-s5) << 5) + (signum_(-s6) << 6) + (signum_(268435456 - s7) << 7)) >> 8


if __name__ == "__main__":
    if len(sys.argv) <= 1:
        print"--------------------------------------------------------"
        print"MiniNero, a reimplementation of CryptoNote one-time ring"
        print"                  signatures in Python"
        print""
        print"         Copyright (c) 2014, The Monero Project"
        print"--------------------------------------------------------"
        print""
        print"Note: MiniNero produces working and valid ring"
        print"signatures, although they differ slightly from the"
        print"Monero ring signatures due to hashing and packing"
        print"differences between the libraries used."
        print""
        print"Run mininero.py with one of the below options"
        print"eg. python mininero.py ringsig"
        print""
        print"rs - demos MiniNero the random_scalar function"
        print"keys - demos MiniNero key generation"
        print"fasthash - demos MiniNero fast hash "
        print"hashscalar - demos MiniNero H_s(P) equivalent function" 
        print"hashcurve - demos MiniNero H_p(P)"
        print"checkkey - demos MiniNero check_key function"
        print"secpub - demos turning a fixed secret to a public key"
        print"keyder - demos MiniNero Key Derivation"
        print"dersca - demos MiniNero derivation to scalar"
        print"derpub - demos derive public key"
        print"dersec - demos derive secret key"
        print"testcomm - tests the helper struct for some CN functions"
        print"gensig - testing generate_signature"
        print"checksig - tests checking signature"
        print"keyimage - tests creating I=xH_p(P)"
        print"ringsig - tests creating and checking a ringsig"
        print"conv - tests some helper conversion functions"
        print"red - tests some of the sc_Code"
        print"gedb - tests some of the edwards curve functions"
        print"sck - tests sc_check"
        quit()
    if sys.argv[1] == "rs":
        #test random_scalar
        print(longToHex(random_scalar()))
    if sys.argv[1] == "keys":
        #test generating keys
        x,P = generate_keys()
        print"generating keys:"
        print("secret:")
        print( x)
        print("public:")
        print( P)
        print("the point P")
        print(decodepoint(P.decode("hex")))
    if sys.argv[1] == "fasthash":
        mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14"
        output_index = 2
        buf2 = struct.pack('64sl', mysecret, output_index) 
        #buf2 = pickle(buf)
        #print(buf2)
        print(buf2)
        print(cn_fast_hash(mysecret, len(mysecret)))
        print(cn_fast_hash(buf2, len(buf2)))
 
    if sys.argv[1] == "hashscalar":
        data = "ILOVECATS"
        print(cn_fast_hash(data, len(data)))
        print(hash_to_scalar(data, len(data)))
    if sys.argv[1] == "hashcurve":
        data = "ILOVECATS"
        print(cn_fast_hash(data, len(data)))
        print(hash_to_ec(data))

    if sys.argv[1] == "checkkey":
        x, P = generate_keys()
        print(check_key(P))        
    if sys.argv[1] == "secpub":
        #testing for secret_key_to_public_key
        #these test vecs were for the monty implementation
        mysecret = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14"
        mypublic = "b1c652786697a5feef36a56f36fde524a21193f4e563627977ab515f600fdb3a"
        mysecret, P = generate_keys() 
        pub2 = secret_key_to_public_key(mysecret)
        print(pub2.encode("hex"))
    if sys.argv[1] == "keyder":
        #testing for generate_key_derivation
        x,P = generate_keys()
        print(x, P)
        print(generate_key_derivation(P, x))

    if sys.argv[1] == "dersca":
        #testing for derivation_to_scalar
        #this is getting a scalar for one-time-keys rH_s(P)
        aa, AA = generate_keys()
        bb, BB = generate_keys()
        for i in range(0,3):
            rr, ZZ = generate_keys()
            derivation = generate_key_derivation(BB, aa)
            s = derivation_to_scalar(derivation, i)
            print(s)
    if sys.argv[1] == "derpub":
        x, P = generate_keys()
        output_index = 5
        keyder = generate_key_derivation(P, x)
        print("keyder", keyder)
        print(derive_public_key(keyder, output_index, P))
    if sys.argv[1] == "dersec":
        x, P = generate_keys()
        output_index = 5
        keyder = generate_key_derivation(P, x)
        print("keyder", keyder)
        print(derive_secret_key(keyder, output_index, x))
    if sys.argv[1] == "testcomm":
        a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14"
        co2 = struct.pack('hhl', 1, 2, 3)
        print(co2.encode("hex")) #sometimes doesn't print if your terminal doesn't have unicode
        
    if sys.argv[1] == "gensig":
        #testing generate_signature
        print""
        prefix = "destination"
        sec, pub  = generate_keys() # just to have some data to use ..
        print(generate_signature(prefix, pub, sec))
    if sys.argv[1] == "checksig":
        prefix = "destination"
        sec, pub  = generate_keys() # just to have some data to use ..
        sir, sic = generate_signature(prefix, pub, sec)
        print(sir, sic)
        print(check_signature(prefix, pub, sir, sic))
    if sys.argv[1] == "keyimage":
        x, P = generate_keys()
        xb = 14662008266461539177776197088974240017016792645044069572180060425138978088469
        Pb = "1d0ecd1758a685d88b39567f491bc93129f59c7dae7182bddc4e6f5ad38ba462"

        I = generate_key_image(Pb, xb)
        print(I) 
    if sys.argv[1] == "ringsig":
        #these are fixed since my computer runs out of memory
        xa = 54592381732429499113512315392038591381134951436395595620076310715410049314218
        Pa = "3c853b5a82912313b179e40d655003c5e3112c041fcf755c3f09d2a8c64d9062"
        xb = 14662008266461539177776197088974240017016792645044069572180060425138978088469
        Pb = "1d0ecd1758a685d88b39567f491bc93129f59c7dae7182bddc4e6f5ad38ba462"
        ima = "0620b888780351a3029dfbf1a5c45a89816f118aa63fa807d51b959cb3c5efc9"
        ima, sic, sir = generate_ring_signature("dest", ima, [Pa, Pb],2,  xb, 1)

        print("ima",ima)
        print("sic", sir)
        print("sir", sic)
        print(check_ring_signature("dest", ima, [Pa, Pb], 2, sir, sic))

    if sys.argv[1] == "conv":
        #testing reduction
        a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14"
        print(a)
        r = hexToLong(a)
        print(r)
        a = longToHex(r)
        print(a)
    if sys.argv[1] == "red":
        a = "99b66345829d8c05041eea1ba1ed5b2984c3e5ec7a756ef053473c7f22b49f14"
        tmp = rand.getrandbits(64 * 8)
        tmp2 = longToHex(tmp)
        print(tmp2)
        tmp3 = longToHex(sc_reduce(tmp))
        print(tmp3)
        tmp4 = sc_reduce32(CURVE_P + 1)
        print(tmp4)
        tmp5 = sc_reduce(CURVE_P + 1)
        print(tmp5)
    if sys.argv[1] == "gedb":
        x, P = generate_keys()
        print(ge_double_scalarmult_base_vartime(x, P, x))
    if sys.argv[1] == "sck":
        #testing sc_check
        x, P = generate_keys()
        print(sc_check(x))
        print("nonreduced", longToHex(x))
        print("reduced", sc_reduce32_2(x))
        print("check reduced", sc_check(hexToLong(sc_reduce32_2(x))))
