Exemple #1
0
    def sendDepositTimeout(self, txid, defaulter=None):
        '''
        This is sent when one or both parties have failed to deliver
        their deposit to the CNE after contract signing is complete.
        If defaulter is None, it means both have defaulted,
        meaning only a message is sent to mark the transaction as defunct.
        Otherwise defaulter marks the role of the defaulter and the
        other party's deposit is returned.
        '''
        tx = self.getTxByID(txid)

        if defaulter and defaulter not in [tx.buyer, tx.seller]:
            shared.debug(0, ["Critical error: wrong argument"])

        defaultedList = ','.join([tx.buyer, tx.seller
                                  ]) if not defaulter else defaulter

        for r in [tx.buyer, tx.seller]:
            self.sendMessage('CNE_DEPOSIT_TIMEOUT:' + defaultedList,
                             recipientID=r,
                             txID=txid)

        for a in [[tx.buyer, tx.buyerInitialDeposits],
                  [tx.seller, tx.sellerInitialDeposits]]:
            if a[1]:  #only need to spend back if there are any deposits
                #we want to spend these utxos back to the depositor, minus txfee
                shared.debug(
                    0, ["For agent:", a[0], "were refunding this:", a[1]])
                multisig.spendUtxosDirect(self.uniqID(), self.uniqID(), a[0],
                                          a[1])

        #rewind actions completed as necessary; make the transaction defunct
        self.transactionUpdate(tx=tx, new_state=212)
Exemple #2
0
def getSingleMessage(recipientID, timeout=1, chanIndex=0, external=False):
    if external:
        global chanExternal, connExternal
    else:
        global chan
    chanTmp = chan[chanIndex] if not external else chanExternal

    chanTmp.queue_declare(queue=recipientID)
    for i in range(1, timeout + 1):
        #if timeout>1:
        time.sleep(1)
        method_frame,header_frame,body = chanTmp.basic_get(queue=recipientID,\
                                                        no_ack=True)
        if not method_frame:
            continue
        else:
            if '|' not in body:
                shared.debug(
                    0, ["Format error in message:", body, ";message ignored"])
                return None
            else:
                msg = body.split('|')
                #bugfix 8 Oct 2013: there can be a | in the message!!
                #print "message wrapper is receiving:",'|'.join(msg[1:])
                shared.debug(
                    5, ["in message layer got:", msg[0], '|'.join(msg[1:])])
                return {msg[0]: '|'.join(msg[1:])}
Exemple #3
0
def editcap_inner(args, frames, outfile):
    start_window = 0
    filenames = []

    while (start_window + MAX_FRAME_FILTER < len(frames)):
        tmpargs = []
        tmpargs.extend(args)
        shared.debug(1,["starting an editcap run with start_window: " \
                    ,str(start_window)])
        filename = outfile + ".tmp." + str(start_window)
        filenames.append(filename)
        tmpargs.append(filename)
        tmpargs.extend(frames[start_window:start_window + MAX_FRAME_FILTER])
        #shared.debug(1,["Here is the call to editcap: ", tmpargs])
        shared.debug(2, ["Calling editcap with these arguments: ", tmpargs])
        shared.debug(4, subprocess.check_output(tmpargs))
        start_window += MAX_FRAME_FILTER

    args.append(outfile + ".tmp." + str(start_window))
    args.extend(frames[start_window:])
    shared.debug(2, ["Calling editcap with these arguments: ", args])
    shared.debug(4, subprocess.check_output(args))
    #Lastly, need to concatenate and delete all the temporary files
    args = [g("Exepaths", "mergecap_exepath"), '-w', outfile]
    args.extend(filenames)
    subprocess.check_output(args)

    for filename in filenames:
        os.remove(filename)
Exemple #4
0
 def getCounterparty(self,requester):
     buyer,seller = [self.text['Buyer BTC Address'],self.text['Seller BTC Address']]
     if requester not in [buyer,seller]:
         shared.debug(1,["Error, this contract does not contain",requester])
         return None
     ca = buyer if seller==requester else seller 
     return ca        
Exemple #5
0
 def receiveSSLKeysAndSendHtml(self, msg):
     #get the transaction first
     tx = self.getTxByID(msg[0].split('.')[0])
     shared.debug(0,["Received these keys:",msg])
     #grab the keys from the message and turn them into a single 
     #keyfile for use by tshark
     sslkeylines = msg[1].split(':')[1].split(',')[1:]
     keydir = os.path.join(g("Directories","escrow_base_dir"),\
             '_'.join(['escrow',tx.uniqID(),'banksession']))
     kf = os.path.join(keydir,'buyer_sent.keys')
     with open(kf,'w') as f:
         for kl in sslkeylines:
             f.write(kl)
         f.close()
     
     #keys have been committed to disk:
     self.transactionUpdate(tx=tx,new_state=801)
     
     #now we can use user_sent.keys as our input to tshark
     stcpdir=os.path.join(keydir,'stcplog')
     merged_trace = os.path.join(stcpdir,'merged.pcap')
     sharkutils.mergecap(merged_trace,stcpdir,dir=True)
     htmlarray = sharkutils.get_all_html_from_key_file(capfile=merged_trace,\
                                                       keyfile=kf)
     #for user security, delete keys immediately ? TODO
     
     #send html to super escrow for adjudication TODO
     shared.debug(0,["Sending html to super escrow for this transaction"])
     m_k = tx.uniqID()+'.'+self.escrowID
     for a in htmlarray:
         self.sendMessage('DISPUTE_HTML_EVIDENCE:'+\
                            str(a),recipientID='ADJ'+self.uniqID(),txID=tx.uniqID())
         
     #arbiter has been notified; final action requires human intervention
     self.transactionUpdate(tx=tx,new_state=802)
Exemple #6
0
def mergecap(outfile, infiles, dir=False):

    outfile = shared.verify_file_creation(outfile, \
                    "mergecap output already exists!",\
                        overwrite=True,prompt=False,remove_in_advance=True)
    args = [g("Exepaths", "mergecap_exepath")]
    if (dir):
        args.extend(['-w', outfile, os.path.join(infiles, '*')])
    else:
        args.extend(['-w', outfile])
        args.extend(infiles)
    shared.debug(0, ["mergecap call is:", args])
    try:
        #bug discovered 29 Sep 2013: wildcards don't get read in Unix without
        #passing through the shell interpreter - does this muck up Windows?
        if shared.OS == 'Windows':
            return subprocess.check_output(args)
        elif shared.OS == 'Linux':
            return subprocess.check_output(' '.join(args), shell=True)
        else:
            print "Unrecognised OS"
            exit(1)
    except:
        shared.debug(0, ["Error in mergecap execution, quitting!"])
        exit(1)
Exemple #7
0
def get_all_ssl_hashes_from_capfile(capfile, handshake= False, port= -1, \
                                    stcp_flag=False,stream='',in_options=[]):
    hashes = []
    for options in build_option_list(in_options=in_options):
        if stcp_flag:
            
            #here "capfile" is not actually a file, it's the directory
            #containing all the per-stream captures.
            #(stcppipe logs multiple capfiles, one per stream)
            
            
            #6 Sep 2013: The following is to merge the stcppipe log files into one pcap
            #unfortunately, stcppipe currently does not mark separate stream numbers
            #(all tcp streams have stream index 0) and so the merged file CANNOT
            #be used currently.
            #Update 8 Sep 2013 updated stcppipe called stcppipe_port solves
            #problem by using client port so that merged file now has
            #separate streams. Can still use 'basic' stcppipe, but it will be slower
            #edited 17th Sept - we have abandoned 'non-port' stcppipe.
            shared.debug(1,[\
    "stcppipe_port found; merging all streams from stcppipe for processing.."])
            merged_stcp_file = os.path.join(capfile,"merged.pcap") #TODO: magic string?
            shared.debug(1,["merged stcppipe filename is:",merged_stcp_file])
            mergecap(merged_stcp_file,capfile,dir=True)
            return get_ssl_hashes_from_capfile(capfile=merged_stcp_file,\
                                               port=port,options=options)
                
        else:
            hashes.extend(get_ssl_hashes_from_capfile(capfile=capfile,port=port,\
                                               stream=stream,options=options))
    return hashes
Exemple #8
0
    def releaseFunds(self, transaction, toBuyer, sig):
        '''Provide signature for multisig release to buyer
        if 'toBuyer' is true then send tx to network
        Also return deposit to buyer and seller
        TODO: if toBuyer is false, release to seller 
        '''
        #construct all pubkeys:
        pubBuyer = transaction.getCtrprtyPubkey(True)
        pubSeller = transaction.getCtrprtyPubkey(False)
        pubEscrow = g("Escrow",
                      "escrow_pubkey")  #this config is fixed on the escrow
        receiver = transaction.buyer if toBuyer else transaction.seller
        sig2 = multisig.createSigForRedemptionRaw(pubEscrow, pubBuyer, pubSeller,\
                                                  transaction.sellerFundingTransactionHash,\
                                                 receiver)

        pubs = [pubBuyer, pubSeller, pubEscrow]
        #for now, the sig array has ONE element, corresponding to the
        #seller funding transaction hash
        #TODO: add another 1/2 transactions for deposit redemption
        sigArray = [[[sig, sig2], [pubSeller, pubEscrow]]]
        multisig.broadcastToNetworkRaw(sigArray,pubs,transaction.sellerFundingTransactionHash,\
                                      receiver)
        shared.debug(0, [
            "Sent the bitcoins to the buyer; transaction completed successfully!"
        ])
        self.transactionUpdate(tx=transaction, new_state=700)
Exemple #9
0
def mergecap(outfile,infiles,dir=False):
    
    outfile = shared.verify_file_creation(outfile, \
                    "mergecap output already exists!",\
                        overwrite=True,prompt=False,remove_in_advance=True)
    args = [g("Exepaths","mergecap_exepath")]
    if (dir):
        args.extend(['-w',outfile, os.path.join(infiles,'*')])
    else:
        args.extend(['-w',outfile])
        args.extend(infiles)
    shared.debug(0,["mergecap call is:",args])
    try:
        #bug discovered 29 Sep 2013: wildcards don't get read in Unix without
        #passing through the shell interpreter - does this muck up Windows?
        if shared.OS=='Windows':
            return subprocess.check_output(args)
        elif shared.OS=='Linux':
            return subprocess.check_output(' '.join(args),shell=True)
        else:
            print "Unrecognised OS"
            exit(1)
    except:
        shared.debug(0,["Error in mergecap execution, quitting!"])
        exit(1)
Exemple #10
0
def editcap_inner(args,frames,outfile):
        start_window = 0
        filenames = []
       
        while (start_window+MAX_FRAME_FILTER < len(frames)):
            tmpargs = []
            tmpargs.extend(args)
            shared.debug(1,["starting an editcap run with start_window: " \
                        ,str(start_window)])
            filename = outfile+".tmp."+str(start_window)
            filenames.append(filename)
            tmpargs.append(filename)
            tmpargs.extend(frames[start_window:start_window+MAX_FRAME_FILTER])
            #shared.debug(1,["Here is the call to editcap: ", tmpargs])
            shared.debug(2,["Calling editcap with these arguments: ",tmpargs])
            shared.debug(4,subprocess.check_output(tmpargs))
            start_window += MAX_FRAME_FILTER
        
        args.append(outfile+".tmp."+str(start_window))
        args.extend(frames[start_window:])
        shared.debug(2,["Calling editcap with these arguments: ",args])
        shared.debug(4,subprocess.check_output(args))
        #Lastly, need to concatenate and delete all the temporary files
        args = [g("Exepaths","mergecap_exepath"),'-w',outfile]
        args.extend(filenames)
        subprocess.check_output(args)
       
        for filename in filenames:
            os.remove(filename) 
Exemple #11
0
def editcap(infile, outfile, reverse_flag = 0, filter='', frames=[]):
    editcap_exepath =  g("Exepaths","editcap_exepath")
    frame_list=[]
    if reverse_flag:
        args = [editcap_exepath, '-r', infile]
    else:
        args = [editcap_exepath,infile]
    
    if (frames):
        frame_list = frames
    else:
        tshark_out = tshark(infile,field = 'frame.number',filter = filter)
        frame_list = shared.pisp(tshark_out)
    
    shared.debug(3,["Frames are: ", frame_list]) 
    
    #TODO: This won't work if -r flag not used;
    #may need some reconsideration
    if (len(frame_list)>MAX_FRAME_FILTER):
        editcap_inner(args,frame_list,outfile)
    else:
        args.append(outfile)    
        args.extend(frame_list)
        shared.debug(2,["Calling editcap with these arguments: ",args])
        subprocess.check_output(args)
Exemple #12
0
def build_option_list(in_options=[], base='http'):
    if not in_options:
        return [[]]

    option_boolean_lists = []
    #build a list of possible options flags to try
    if (in_options):
        for option in in_options:
            if 'port' in option:  #very ugly hack just to try
                option_boolean_lists.append([option, ''])
            else:
                option_boolean_lists.append(
                    [option + ':True', option + ':False'])
        option_all_lists = list(itertools.product(*option_boolean_lists))
        shared.debug(3, option_all_lists)
    else:
        option_all_lists = []

    options_permutations_list = []

    for option_list in option_all_lists:
        option_list_tmp = filter(None, list(option_list))
        options_permutations_list.append(option_list_tmp)

    return options_permutations_list
Exemple #13
0
 def __init__(self,basedir,btcaddress):
     super(EscrowAgent,self).__init__(basedir=basedir, btcadd=btcaddress)
     shared.debug(1,[\
     "instantiating an escrow agent, listening for messages"])
     
     #messaging server should always be local for escrow
     self.host='127.0.0.1'
     
     #log in to message queue server
     Msg.instantiateConnection(un=g("Agent","agent_rabbitmq_user"),pw=g("Agent","agent_rabbitmq_pass"))
     
     #hardcoded for testing TODO
     self.escrowID=btcaddress
     
     #self.superID = g("Escrow","super_id")
     
     #get the public list of escrows for propagation to RE
     self.escrowList = self.getEscrowList()
     
     #this  needs to be persisted as it contains
     #state information - in order to accept requests involving two parties,
     #the escrow needs to keep a record of earlier requests downloaded
     #from the MQ. The format is a list of lists, each inner list having
     #a key,message pair [k,m]
     self.requestStore=[]
     
     d = os.path.join(g("Directories","escrow_base_dir"),"multisig_store")
     p = g("Escrow","escrow_pubkey")
     #initialise multisig
     multisig.initialise(p,d)        
Exemple #14
0
def get_all_ssl_hashes_from_capfile(capfile, handshake= False, port= -1, \
                                    stcp_flag=False,stream='',in_options=[]):
    hashes = []
    for options in build_option_list(in_options=in_options):
        if stcp_flag:

            #here "capfile" is not actually a file, it's the directory
            #containing all the per-stream captures.
            #(stcppipe logs multiple capfiles, one per stream)

            #6 Sep 2013: The following is to merge the stcppipe log files into one pcap
            #unfortunately, stcppipe currently does not mark separate stream numbers
            #(all tcp streams have stream index 0) and so the merged file CANNOT
            #be used currently.
            #Update 8 Sep 2013 updated stcppipe called stcppipe_port solves
            #problem by using client port so that merged file now has
            #separate streams. Can still use 'basic' stcppipe, but it will be slower
            #edited 17th Sept - we have abandoned 'non-port' stcppipe.
            shared.debug(1,[\
    "stcppipe_port found; merging all streams from stcppipe for processing.."])
            merged_stcp_file = os.path.join(
                capfile, "merged.pcap")  #TODO: magic string?
            shared.debug(1, ["merged stcppipe filename is:", merged_stcp_file])
            mergecap(merged_stcp_file, capfile, dir=True)
            return get_ssl_hashes_from_capfile(capfile=merged_stcp_file,\
                                               port=port,options=options)

        else:
            hashes.extend(get_ssl_hashes_from_capfile(capfile=capfile,port=port,\
                                               stream=stream,options=options))
    return hashes
Exemple #15
0
 def sendDepositTimeout(self,txid,defaulter=None):
     '''
     This is sent when one or both parties have failed to deliver
     their deposit to the CNE after contract signing is complete.
     If defaulter is None, it means both have defaulted,
     meaning only a message is sent to mark the transaction as defunct.
     Otherwise defaulter marks the role of the defaulter and the
     other party's deposit is returned.
     '''
     tx = self.getTxByID(txid)
     
     if defaulter and defaulter not in [tx.buyer,tx.seller]:
         shared.debug(0,["Critical error: wrong argument"])
         
     defaultedList = ','.join([tx.buyer,tx.seller]) if not defaulter else defaulter
     
     for r in [tx.buyer,tx.seller]:
         self.sendMessage('CNE_DEPOSIT_TIMEOUT:'+defaultedList,recipientID=r,txID=txid)
     
     
     for a in [[tx.buyer,tx.buyerInitialDeposits],[tx.seller,tx.sellerInitialDeposits]]:
         if a[1]: #only need to spend back if there are any deposits
             #we want to spend these utxos back to the depositor, minus txfee
             shared.debug(0,["For agent:",a[0],"were refunding this:",a[1]])
             multisig.spendUtxosDirect(self.uniqID(),self.uniqID(),a[0],a[1])
         
     #rewind actions completed as necessary; make the transaction defunct
     self.transactionUpdate(tx=tx,new_state=212)
Exemple #16
0
def editcap(infile, outfile, reverse_flag=0, filter='', frames=[]):
    editcap_exepath = g("Exepaths", "editcap_exepath")
    frame_list = []
    if reverse_flag:
        args = [editcap_exepath, '-r', infile]
    else:
        args = [editcap_exepath, infile]

    if (frames):
        frame_list = frames
    else:
        tshark_out = tshark(infile, field='frame.number', filter=filter)
        frame_list = shared.pisp(tshark_out)

    shared.debug(3, ["Frames are: ", frame_list])

    #TODO: This won't work if -r flag not used;
    #may need some reconsideration
    if (len(frame_list) > MAX_FRAME_FILTER):
        editcap_inner(args, frame_list, outfile)
    else:
        args.append(outfile)
        args.extend(frame_list)
        shared.debug(2, ["Calling editcap with these arguments: ", args])
        subprocess.check_output(args)
Exemple #17
0
def getSingleMessage(recipientID,timeout=1,chanIndex=0,external=False):
    if external:
        global chanExternal,connExternal
    else:
        global chan
    chanTmp = chan[chanIndex] if not external else chanExternal
    
    chanTmp.queue_declare(queue=recipientID)
    for i in range(1,timeout+1):
        #if timeout>1:
        time.sleep(1)
        method_frame,header_frame,body = chanTmp.basic_get(queue=recipientID,\
                                                        no_ack=True)
        if not method_frame:
            continue
        else:
            if '|' not in body:
                shared.debug(0,["Format error in message:",body,";message ignored"])
                return None
            else:
                msg = body.split('|')
                #bugfix 8 Oct 2013: there can be a | in the message!!
                #print "message wrapper is receiving:",'|'.join(msg[1:])
                shared.debug(5,["in message layer got:",msg[0],'|'.join(msg[1:])])
                return {msg[0]:'|'.join(msg[1:])}
Exemple #18
0
    def receiveSSLKeysAndSendHtml(self, msg):
        #get the transaction first
        tx = self.getTxByID(msg[0].split('.')[0])
        shared.debug(0, ["Received these keys:", msg])
        #grab the keys from the message and turn them into a single
        #keyfile for use by tshark
        sslkeylines = msg[1].split(':')[1].split(',')[1:]
        keydir = os.path.join(g("Directories","escrow_base_dir"),\
                '_'.join(['escrow',tx.uniqID(),'banksession']))
        kf = os.path.join(keydir, 'buyer_sent.keys')
        with open(kf, 'w') as f:
            for kl in sslkeylines:
                f.write(kl)
            f.close()

        #keys have been committed to disk:
        self.transactionUpdate(tx=tx, new_state=801)

        #now we can use user_sent.keys as our input to tshark
        stcpdir = os.path.join(keydir, 'stcplog')
        merged_trace = os.path.join(stcpdir, 'merged.pcap')
        sharkutils.mergecap(merged_trace, stcpdir, dir=True)
        htmlarray = sharkutils.get_all_html_from_key_file(capfile=merged_trace,\
                                                          keyfile=kf)
        #for user security, delete keys immediately ? TODO

        #send html to super escrow for adjudication TODO
        shared.debug(0, ["Sending html to super escrow for this transaction"])
        m_k = tx.uniqID() + '.' + self.escrowID
        for a in htmlarray:
            self.sendMessage('DISPUTE_HTML_EVIDENCE:'+\
                               str(a),recipientID='ADJ'+self.uniqID(),txID=tx.uniqID())

        #arbiter has been notified; final action requires human intervention
        self.transactionUpdate(tx=tx, new_state=802)
Exemple #19
0
def sendAdjudicatorCollateralSigningRequest(myself,identityInfo,collateralAmount):
    
    msigaddr,mscript = myself.createCollateralMultisig()
    if not msigaddr or not mscript:
        shared.debug(0,["Critical error: cannot create a multisignature address."])
        return False
    mypub,mypriv = multisig.getKeysFromUniqueID(myself.uniqID())
    
    shared.debug(0,["You have created this multisig address:",msigaddr])
    shared.debug(0,["Please wait while your request is sent out to the pool."])
    #broadcast
    #TODO this is slow but ..?
    for i,e in enumerate(getEscrowList()):
        
        shared.config.set("Escrow","escrow_id",value=e[0])
        shared.config.set("Escrow","escrow_pubkey",value=e[2])
        shared.config.set("Escrow","escrow_host",value=e[1])
        shared.debug(2,["Set the escrow to host:",g("Escrow","escrow_host"),"id:",g("Escrow","escrow_id"),"pubkey:",g("Escrow","escrow_pubkey")])                    
        
        #Msg.closeConnection()
        time.sleep(1)
        
        Msg.instantiateConnection()
        
        myself.sendMessage('ADJUDICATOR_APPLICATION:'+'|'.join([msigaddr,mypub,identityInfo]))
    shared.debug(0,["Your application has been sent to all pool members. Please wait for their response."])
    return True
Exemple #20
0
 def allAdjudicatorsAcceptedApplication(self):
     with open(g("Escrow","adjudicator_store")) as f:
         content = f.readlines()
     filtered = [x for x in content if 'APPLICATION FOR ADJUDICATION ACCEPTED BY' in x]
     identities = [x.split(',')[0].split(':')[1] for x in filtered]
     adjudicatorIdentities = [x[0] for x in [e.split('|') for e in g("Escrow","escrow_list").split(',')]]
     if set(adjudicatorIdentities) != set(identities):
         shared.debug(5,["Not all adjudicators have yet accepted the application"])
         return
     #we are ready to make the collateral payment
     rspns = shared.get_binary_user_input('Your application for adjudicator role has been accepted. Are you ready to make the payment of '+g("Escrow","escrow_collateral_size")+" satoshis?",'y','y','n','n')
     if rspns != 'y':
         return
     #create the transaction and present it to the user for verification
     #we must be VERY careful here as the amount is large
     msigaddr, mscript = self.createCollateralMultisig()
     if not msigaddr or not mscript:
         raise Exception("This should not be possible.")
     
     collSize = g("Escrow","escrow_collateral_size")
     multisig.spendUtxos(self.uniqID(),self.uniqID(), msigaddr, None, amt=int(collSize),prepare=True)
     rspns = shared.get_binary_user_input('Do you still want to pay?','y','y','n','n')
     if rspns != 'y':
         return
     #we have confirmation after checking; broadcast
     multisig.spendUtxos(self.uniqID(),self.uniqID(),msigaddr,None,amt=int(collSize))
     
     shared.debug(0,["Congratulations, you are now a member of the pool. Please contact the other pool members on instructions for setting up the server."])
Exemple #21
0
    def __init__(self, basedir, btcaddress):
        super(EscrowAgent, self).__init__(basedir=basedir, btcadd=btcaddress)
        shared.debug(1,[\
        "instantiating an escrow agent, listening for messages"])

        #messaging server should always be local for escrow
        self.host = '127.0.0.1'

        #log in to message queue server
        Msg.instantiateConnection(un=g("Agent", "agent_rabbitmq_user"),
                                  pw=g("Agent", "agent_rabbitmq_pass"))

        #hardcoded for testing TODO
        self.escrowID = btcaddress

        #self.superID = g("Escrow","super_id")

        #get the public list of escrows for propagation to RE
        self.escrowList = self.getEscrowList()

        #this  needs to be persisted as it contains
        #state information - in order to accept requests involving two parties,
        #the escrow needs to keep a record of earlier requests downloaded
        #from the MQ. The format is a list of lists, each inner list having
        #a key,message pair [k,m]
        self.requestStore = []

        d = os.path.join(g("Directories", "escrow_base_dir"), "multisig_store")
        p = g("Escrow", "escrow_pubkey")
        #initialise multisig
        multisig.initialise(p, d)
Exemple #22
0
 def getCtrprtyPubkey(self, c):
     addr = self.buyer if c else self.seller
     msg = self.contract.getContractTextOrdered()
     for sig in self.contract.signatures.values():
         pub = multisig.ecdsa_recover(msg, sig)
         if addr == multisig.pubtoaddr(pub):
             shared.debug(2, ["Got address", addr, "for pubkey,", pub])
             return pub
Exemple #23
0
 def __init__(self, contractDetailsDict):
     shared.debug(5, ["\n instantiating a contract \n"])
     self.text = contractDetailsDict
     #hash the contents
     self.setHash()
     #no signature on creation
     self.isSigned = False
     #allow multiple signatures "appended"
     self.signatures = {}
Exemple #24
0
 def sendConfirmationBankingSessionEnded(self,tx,rspns):
     #sanity check
     if tx.getRole(self.uniqID()) != 'buyer':
         shared.debug(0,["Error: user agent:",self.agent.uniqID(),\
     "is not the buyer for this transaction and so can't confirm the end",\
         "of the banking session!"])  
     #construct a message to the escrow
     shared.debug(0,["Sending bank session end confirm to seller and escrow"])
     self.sendMessage('RE_BANK_SESSION_ENDED:'+rspns,recipientID='RE',txID=tx.uniqID())
Exemple #25
0
 def __init__(self,contractDetailsDict):
     shared.debug(5,["\n instantiating a contract \n"])
     self.text = contractDetailsDict 
     #hash the contents
     self.setHash()
     #no signature on creation
     self.isSigned=False
     #allow multiple signatures "appended"
     self.signatures={}    
Exemple #26
0
    def receiveContractCNE(self, msg):
        '''When receiving a contract, first check it's signed and
            throw it out if not. Otherwise, store it in the list of contracts 
            that have been proposed by possible counterparties
            We can choose to accept at anytime, within the process/session.
            However we will not persist contract 'suggestions' across sessions. '''

        allContractDetails = ':'.join(msg.split(':')[1:]).split('|')
        contractDetails = allContractDetails[0]
        #the contract is in json; need to change it to a Contract object
        contractDetailsDict = json.loads(contractDetails)
        tmpContract = Contract.Contract(contractDetailsDict)

        ca = tmpContract.getCounterparty(self.uniqID())

        if not ca:
            return 'Contract invalid: does not contain this identity'

        for s in allContractDetails[1:]:
            ad = multisig.pubtoaddr(
                multisig.ecdsa_recover(tmpContract.getContractTextOrdered(),
                                       s))
            shared.debug(2, ["\n recovery produced this address: ", ad, "\n"])
            tmpContract.signatures[ad] = s

        #now the temporary contract object is fully populated;
        #we can check the signatures match the IDs in the contract
        for k, v in tmpContract.signatures.iteritems():
            if k not in [
                    tmpContract.text['Buyer BTC Address'],
                    tmpContract.text['Seller BTC Address']
            ]:
                shared.debug(1,
                             ['Error: signature', v, 'from', k, 'was invalid'])
                return 'Invalid contract signature'

        self.contractLock.acquire()
        try:
            #note that this represents an override for
            #repeated sending of contracts; one cp can only
            #be suggesting one contract at a time
            self.pendingContracts[ca] = tmpContract
        finally:
            self.contractLock.release()
        #if the contract is already signed by me AND ctrprty, send it to escrow
        if len(tmpContract.signatures.keys()) > 1:
            self.contractLock.acquire()
            try:
                self.workingContract = tmpContract
                #wipe the pending contract list; we are only
                #interested in the live contract now
                self.pendingContracts = {}
            finally:
                self.contractLock.release()

        return 'Signed contract successfully received from counterparty: ' + ca
Exemple #27
0
 def sendTransactionSynchronization(self,msg):
     requester = msg[0].split('.')[1]
     shared.debug(0,["Requester:",requester])
     smsg_key = '0.'+self.uniqID()
     for tx in self.transactions:
         if requester == tx.buyer or requester == tx.seller:
             self.sendMessage('RE_TRANSACTION_SYNC_RESPONSE:'\
                              +pickle.dumps(tx),recipientID=requester,txID=tx.uniqID())
     #send a final message to mark end of list
     self.sendMessage('RE_TRANSACTION_SYNC_COMPLETE:',recipientID=requester,txID=tx.uniqID())
Exemple #28
0
 def getEscrowList(self):
         eL = g("Escrow","escrow_list").split(',')
         y=[]
         for i,x in enumerate(eL):
             d = x.split('|')
             y.append({'host':d[1]})
             y[i]['pubkey']=d[2]
             y[i]['id']=d[0]
         shared.debug(4,["Generated this escrow list:",y])
         return y 
Exemple #29
0
 def getEscrowList(self):
     eL = g("Escrow", "escrow_list").split(',')
     y = []
     for i, x in enumerate(eL):
         d = x.split('|')
         y.append({'host': d[1]})
         y[i]['pubkey'] = d[2]
         y[i]['id'] = d[0]
     shared.debug(4, ["Generated this escrow list:", y])
     return y
Exemple #30
0
 def getCounterparty(self, requester):
     buyer, seller = [
         self.text['Buyer BTC Address'], self.text['Seller BTC Address']
     ]
     if requester not in [buyer, seller]:
         shared.debug(1,
                      ["Error, this contract does not contain", requester])
         return None
     ca = buyer if seller == requester else seller
     return ca
Exemple #31
0
    def startRandomEscrowChoice(self, txhash):
        #first upgrade transaction. then notify counterparties.
        #then take timestamp for 1 or 2 minutes after deposits confirmed.
        #read from NIST beacon. convert to small rand in range.
        #message to counterparties and RE of choice.

        #mark the waypoint time. This time must be *after* the
        #arrival of both deposits; this is causally guaranteed by the protocol
        #note that this is not yet perfect TODO - we either change it
        #or somehow guarantee synchronisation between client and server
        waypoint = int(time.time())

        tx = self.getTxByID(txhash)
        self.transactionUpdate(full=False, txID='', tx=tx, new_state=206)
        m = 'CNE_RE_CHOICE_STARTED:' + str(waypoint)
        for recipient in [tx.buyer, tx.seller]:
            self.sendMessage(m, recipientID=recipient, txID=txhash)

        #get public random number
        publicRandomMax = 1000000
        timestamp, publicRandom = shared.get_public_random(publicRandomMax)

        numEscrows = len(self.escrowList)
        chosenEscrow = int(
            math.floor((numEscrows / publicRandomMax) * publicRandom))

        shared.debug(0,
                     ["We chose escrow:", chosenEscrow, "at time:", timestamp])

        #TODO hack for testing, remove this
        chosenEscrow = 1

        #generate multisig address on remote escrow
        epk = self.escrowList[chosenEscrow]['pubkey']
        REMultisigAddr, mscript = self.getMultisigAddress(tx, epk)
        shared.debug(0, ["Generated multisig was: ", REMultisigAddr])

        #We include the escrow pubkey in the message so as to confirm
        #the RE identity in case of some error syncing the escrow table
        m = 'CNE_RE_CHOSEN:'+'|'.join([self.escrowList[chosenEscrow]['id'],tx.uniqID(),\
                                       tx.contract.getContractText(),str(epk),REMultisigAddr])
        for recipient in [tx.buyer, tx.seller]:
            self.sendMessage(m, recipientID=recipient, txID=tx.uniqID())
        paidAmount, btcTxhash = self.sendCoinsToRE(txhash, REMultisigAddr)

        tx.depositHash = btcTxhash
        tx.chosenRE = chosenEscrow

        #update the transaction state
        self.transactionUpdate(full=False,
                               txID=tx.uniqID(),
                               tx=None,
                               new_state=300)
        self.transferTxToRE(tx, chosenEscrow)
Exemple #32
0
 def sendTransactionSynchronization(self, msg):
     requester = msg[0].split('.')[1]
     shared.debug(0, ["Requester:", requester])
     smsg_key = '0.' + self.uniqID()
     for tx in self.transactions:
         if requester == tx.buyer or requester == tx.seller:
             self.sendMessage('RE_TRANSACTION_SYNC_RESPONSE:'\
                              +pickle.dumps(tx),recipientID=requester,txID=tx.uniqID())
     #send a final message to mark end of list
     self.sendMessage('RE_TRANSACTION_SYNC_COMPLETE:',
                      recipientID=requester,
                      txID=tx.uniqID())
Exemple #33
0
 def sendConfirmationBankingSessionEnded(self, tx, rspns):
     #sanity check
     if tx.getRole(self.uniqID()) != 'buyer':
         shared.debug(0,["Error: user agent:",self.agent.uniqID(),\
     "is not the buyer for this transaction and so can't confirm the end",\
         "of the banking session!"])
     #construct a message to the escrow
     shared.debug(0,
                  ["Sending bank session end confirm to seller and escrow"])
     self.sendMessage('RE_BANK_SESSION_ENDED:' + rspns,
                      recipientID='RE',
                      txID=tx.uniqID())
Exemple #34
0
def getUtxos(payee,payer,arr=False):
    
    filteredUtxos=[]
    if arr:
        total = []
    else:
        total = 0
    h = history(payee)
    #for each transaction in the history, check if the input meets
    #the conditions of the filter
    unspent = [x for x in h if 'spend' not in x.keys()]
    #shared.debug(2,["unspent:",str(len(unspent)),unspent])
    
    x = ea.get_from_electrum([payee],t='a')[0]['result']
    shared.debug(8,["Got this from electrum:",x])
    #build list of heights - to get the raw transaction and deserialize,
    #we need to query the electrum server with a transaction_get, which needs
    #BOTH the height and the transaction hash
    heightsDict={}
    for d in x:
        heightsDict[d['tx_hash']]=d['height']
        
    for i in unspent:
        if payer:
            txh = i['output'].split(':')[0]
            if txh not in heightsDict.keys():
                continue
            rawtx = ea.get_from_electrum([[txh,heightsDict[txh]]],t='t')[0]['result']
            cookedtx = deserialize(rawtx) 
            
            #slightly hack-y but necessary:
            #payers are allowed to use more than one input
            #(so they can sweep up utxos)
            #but a transaction with two DIFFERENT input addresses is not allowed
            if len(cookedtx['ins'])>1:
                for j in cookedtx['ins']:
                    p,s,a = ea.get_address_from_input_script(cookedtx['ins'][0]['script'].decode('hex'))
                    p2,s2,a2 = ea.get_address_from_input_script(j['script'].decode('hex'))
                    if not a == a2:
                        raise Exception("Found an input transaction with more than one payer!")
            
            pubkeys,signatures, addr = \
            ea.get_address_from_input_script(cookedtx['ins'][0]['script'].decode('hex')) 
            if addr != payer:
                continue
            
        filteredUtxos.append(i)
        if arr:
            total.append(i['value'])
        else:
            total += i['value']
        
    return (filteredUtxos,total)
Exemple #35
0
    def receiveContractCNE(self, msg):
        '''acting as CNE, the escrow can receive a doubly-signed
        contract at any time from any party. After verifying that the signatures are valid, and that the deposits 
        are specified correctly in the contract, the address
        for deposits is reported and the contract signed for the third time by the escrow.
        Messages sent to both parties giving them a deadline for deposit. '''
        sender = msg[0].split('.')[1]
        #this special message is delimited by |
        allContractDetails = ':'.join(msg[1].split(':')[1:]).split('|')

        #the contract is in json format
        contractDetails = allContractDetails[0]
        tmpContract = Contract.Contract(json.loads(contractDetails))
        pubs = {}
        for s in allContractDetails[1:]:
            tmpPub = multisig.ecdsa_recover(
                tmpContract.getContractTextOrdered(), s)
            ad = multisig.pubtoaddr(tmpPub)
            shared.debug(2, ["\n recovery produced this address: ", ad, "\n"])
            tmpContract.signatures[ad] = s
            #store the pubkeys for later use
            pubs[ad] = tmpPub

        #immediately check for 2 signatures; otherwise dump immediately
        if len(allContractDetails) != 3:
            return (False, 'Not a valid and fully signed contract, ignoring',
                    tmpContract)

        #now the temporary contract object is fully populated;
        #we can check the signatures match the IDs in the contract
        for k, v in tmpContract.signatures.iteritems():
            if k not in [
                    tmpContract.text['Buyer BTC Address'],
                    tmpContract.text['Seller BTC Address']
            ]:
                shared.debug(1,
                             ['Error: signature', v, 'from', k, 'was invalid'])
                return (False, 'Invalid contract signature', tmpContract)

        #need to check that the proposed deposits follow the business rules
        verdict, reason = self.checkBusinessRulesCNE(tmpContract)
        if not verdict: return (verdict, reason, tmpContract)

        #removed for now.
        #now we're happy that the contract is valid we build the dep multisig
        #multisig.initialise(g("Escrow","escrow_pubkey"),g("Directories","escrow_base_dir"))
        #for a,p in pubs.iteritems():
        #multisig.store_share(p,a)
        #msigaddr, mscript = multisig.create_multisig_address(*pubs.keys())

        return (True, multisig.pubtoaddr(g("Escrow",
                                           "escrow_pubkey")), tmpContract)
Exemple #36
0
 def signContractCNE(self):
     if not self.workingContract:
         shared.debug(0, ["Error: contract not defined."])
         return False
     dummy,sig = multisig.signText(self.uniqID(),\
                 self.workingContract.getContractTextOrdered())
     shared.debug(3, ["Here is the signature we made:", sig])
     self.contractLock.acquire()
     try:
         self.workingContract.sign(self.uniqID(), sig)
     finally:
         self.contractLock.release()
     return True
Exemple #37
0
 def signContractCNE(self):
     if not self.workingContract:
         shared.debug(0,["Error: contract not defined."])  
         return False   
     dummy,sig = multisig.signText(self.uniqID(),\
                 self.workingContract.getContractTextOrdered())
     shared.debug(3,["Here is the signature we made:",sig])
     self.contractLock.acquire()
     try:
         self.workingContract.sign(self.uniqID(),sig)
     finally:
         self.contractLock.release()
     return True
Exemple #38
0
def get_magic_hashes(stcpdir,keyfile,port):
    
    #this data structure will contain ALL GET requests performed under SSL
    #for this banking session
    GETs=[]
    #pass the keyfile as a -o flag to tshark
    options=['ssl.keylog_file:'+keyfile]
    
    #these magic hashes will be sent to escrow; when escrow finds them
    #in his hash list, he will dump all following hashes in that stream.
    #(Detailed explanation of reason deferred to later TODO)
    magic_hashes = []

    for x in os.listdir(stcpdir):
        if x == 'merged.pcap':
            continue
        capfile = os.path.join(stcpdir,x)
        GET_dict = get_GET_http_requests(capfile,options)
        shared.debug(0,["Here is the GET dictionary for the file",capfile,":",GET_dict])
        if not GET_dict:
            continue
        
        if not any(GET_dict):
            #this happens if the stream doesn't contain SSL; just ignore it
            continue
        
        GETs.append(GET_dict)
        #this element of the list 'GETs' corresponds to one file, which means
        #one stream out of stcp. It is a dict which maps
        #frame numbers as keys to GET requests as strings.
        #We could conceivably act differently based on some string matching in
        #the GET, in particular related to the content type that's being requested
        #However the simplest action to take is to use tshark to check if 
        #any HTTP content was returned AFTER the LAST GET in the stream. If not,
        #we mark the hash of the GET request frame as magic, and then pass these
        #magic hashes to escrow, who knows to ignore all hashes that occur after
        #it in that stream.
        
        #get the highest frame number in the dict
        highest_frame = max(GET_dict,key=int)
        shared.debug(0,["In file:",capfile," the highest get frame is:",\
                    highest_frame])
        
        #check for no http-content-type OR no http-last-modified (cache hit)
        # after: this is the signal that
        #the connection was dropped, and we cannot assume the other parties
        #in proxying did NOT get the response.
        #TODO: other possible filters are http.server and tcp.srcport
        #I think in some way it should all work
        fs = 'ssl and (http.content_type or http.last_modified) and (frame.number gt '+highest_frame+')'
        if not tshark(capfile,filter=fs,field='frame.number',options=options):
            #get the ssl hashes of that frame
            ssl_hashes = get_ssl_hashes_from_capfile(capfile,\
                        port=port,frames=[highest_frame],options=options)
            #append it to magic_hashes
            shared.debug(0,["Appending these value to magic_hashes:",ssl_hashes])
            magic_hashes.extend(ssl_hashes)
            
    shared.debug(1,["Here is the full printout of the GET requests:",GETs])
    return magic_hashes
Exemple #39
0
    def allAdjudicatorsAcceptedApplication(self):
        with open(g("Escrow", "adjudicator_store")) as f:
            content = f.readlines()
        filtered = [
            x for x in content
            if 'APPLICATION FOR ADJUDICATION ACCEPTED BY' in x
        ]
        identities = [x.split(',')[0].split(':')[1] for x in filtered]
        adjudicatorIdentities = [
            x[0] for x in
            [e.split('|') for e in g("Escrow", "escrow_list").split(',')]
        ]
        if set(adjudicatorIdentities) != set(identities):
            shared.debug(
                5, ["Not all adjudicators have yet accepted the application"])
            return
        #we are ready to make the collateral payment
        rspns = shared.get_binary_user_input(
            'Your application for adjudicator role has been accepted. Are you ready to make the payment of '
            + g("Escrow", "escrow_collateral_size") + " satoshis?", 'y', 'y',
            'n', 'n')
        if rspns != 'y':
            return
        #create the transaction and present it to the user for verification
        #we must be VERY careful here as the amount is large
        msigaddr, mscript = self.createCollateralMultisig()
        if not msigaddr or not mscript:
            raise Exception("This should not be possible.")

        collSize = g("Escrow", "escrow_collateral_size")
        multisig.spendUtxos(self.uniqID(),
                            self.uniqID(),
                            msigaddr,
                            None,
                            amt=int(collSize),
                            prepare=True)
        rspns = shared.get_binary_user_input('Do you still want to pay?', 'y',
                                             'y', 'n', 'n')
        if rspns != 'y':
            return
        #we have confirmation after checking; broadcast
        multisig.spendUtxos(self.uniqID(),
                            self.uniqID(),
                            msigaddr,
                            None,
                            amt=int(collSize))

        shared.debug(0, [
            "Congratulations, you are now a member of the pool. Please contact the other pool members on instructions for setting up the server."
        ])
Exemple #40
0
 def receiveContractCNE(self,msg):
     '''When receiving a contract, first check it's signed and
         throw it out if not. Otherwise, store it in the list of contracts 
         that have been proposed by possible counterparties
         We can choose to accept at anytime, within the process/session.
         However we will not persist contract 'suggestions' across sessions. '''        
     
     allContractDetails = ':'.join(msg.split(':')[1:]).split('|')
     contractDetails = allContractDetails[0]
     #the contract is in json; need to change it to a Contract object
     contractDetailsDict = json.loads(contractDetails)
     tmpContract = Contract.Contract(contractDetailsDict)
     
     ca = tmpContract.getCounterparty(self.uniqID())
     
     if not ca:
         return 'Contract invalid: does not contain this identity'
     
     for s in allContractDetails[1:]:
         ad = multisig.pubtoaddr(multisig.ecdsa_recover(tmpContract.getContractTextOrdered(),s))
         shared.debug(2,["\n recovery produced this address: ",ad,"\n"])
         tmpContract.signatures[ad]=s
     
     #now the temporary contract object is fully populated; 
     #we can check the signatures match the IDs in the contract
     for k,v in tmpContract.signatures.iteritems():
         if k not in [tmpContract.text['Buyer BTC Address'],tmpContract.text['Seller BTC Address']]:
             shared.debug(1,['Error: signature',v,'from',k,'was invalid'])
             return 'Invalid contract signature'
     
     self.contractLock.acquire()
     try:
         #note that this represents an override for
         #repeated sending of contracts; one cp can only
         #be suggesting one contract at a time
         self.pendingContracts[ca] = tmpContract
     finally:
         self.contractLock.release()
     #if the contract is already signed by me AND ctrprty, send it to escrow
     if len(tmpContract.signatures.keys())>1:
         self.contractLock.acquire()
         try:
             self.workingContract = tmpContract
             #wipe the pending contract list; we are only
             #interested in the live contract now
             self.pendingContracts = {}
         finally:
             self.contractLock.release()
         
     return 'Signed contract successfully received from counterparty: '+ca
Exemple #41
0
 def startRandomEscrowChoice(self,txhash):
     #first upgrade transaction. then notify counterparties.
     #then take timestamp for 1 or 2 minutes after deposits confirmed.
     #read from NIST beacon. convert to small rand in range.
     #message to counterparties and RE of choice.
     
     #mark the waypoint time. This time must be *after* the
     #arrival of both deposits; this is causally guaranteed by the protocol
     #note that this is not yet perfect TODO - we either change it
     #or somehow guarantee synchronisation between client and server
     waypoint = int(time.time())
     
     tx = self.getTxByID(txhash)
     self.transactionUpdate(full=False,txID='',tx=tx,new_state=206)
     m = 'CNE_RE_CHOICE_STARTED:'+str(waypoint)
     for recipient in [tx.buyer,tx.seller]:
         self.sendMessage(m,recipientID=recipient,txID=txhash)
     
     #get public random number
     publicRandomMax=1000000
     timestamp,publicRandom = shared.get_public_random(publicRandomMax)
     
     numEscrows = len(self.escrowList)
     chosenEscrow = int(math.floor((numEscrows/publicRandomMax)*publicRandom))
     
     shared.debug(0,["We chose escrow:",chosenEscrow,"at time:",timestamp])
     
     #TODO hack for testing, remove this
     chosenEscrow=1        
     
     #generate multisig address on remote escrow
     epk = self.escrowList[chosenEscrow]['pubkey']
     REMultisigAddr,mscript = self.getMultisigAddress(tx,epk)
     shared.debug(0,["Generated multisig was: ", REMultisigAddr])
            
     #We include the escrow pubkey in the message so as to confirm
     #the RE identity in case of some error syncing the escrow table
     m = 'CNE_RE_CHOSEN:'+'|'.join([self.escrowList[chosenEscrow]['id'],tx.uniqID(),\
                                    tx.contract.getContractText(),str(epk),REMultisigAddr])
     for recipient in [tx.buyer,tx.seller]:
         self.sendMessage(m,recipientID=recipient,txID=tx.uniqID())
     paidAmount,btcTxhash = self.sendCoinsToRE(txhash,REMultisigAddr)
     
     tx.depositHash = btcTxhash
     tx.chosenRE=chosenEscrow 
     
     #update the transaction state
     self.transactionUpdate(full=False,txID=tx.uniqID(),tx=None,new_state=300)                
     self.transferTxToRE(tx,chosenEscrow)
Exemple #42
0
def spendUtxosDirect(addrOwner,addrOwnerID,payee,utxoList):
    """
    spend a set of utxos as returned from a call to getUtxos()
    to recipient payee from owner addrOwner
    """
    u,total= utxoList
    outs = [{'value':total-shared.defaultBtcTxFee,'address':payee}]
    shared.debug(5,["About to make a transaction with these ins:",u,"and these outs:",outs])
    tmptx = mktx(u,outs)
    pub,priv = getKeysFromUniqueID(addrOwnerID)
    for i,x in enumerate(u):
        tmptx = sign(tmptx,i,priv)        
    rspns = ea.send_tx(tmptx)
    shared.debug(2,["Electrum server sent back:",rspns])
    #in this case we return amount spent for convenience
    return (total,tx_hash(tmptx).encode('hex'))    
Exemple #43
0
def get_ssl_hashes_from_ssl_app_data_list(ssl_app_data_list):
    
    ssl_hashes = []
    for s in ssl_app_data_list:
        #get rid of commas and colons
        #(ssl.app_data comma-delimits multiple SSL segments within the same frame)
        s = s.rstrip()
        s = s.replace(',',' ')
        s = s.replace(':',' ')
        
        if s == '':
            shared.debug(2,["Warning: empty ssl app data string passed for hashing!"])
        else:
            ssl_hashes.append(hashlib.md5(bytearray.fromhex(s)).hexdigest()) 
        
    return list(set(ssl_hashes))
Exemple #44
0
 def transactionUpdate(self, full=False, txID='',tx=None,new_state=''):
     '''To ensure correct persistence, transaction states can only be updated
         via this method.
         If full is set, we just persist the current list with no changes.
         Otherwise, the transaction can be set either with a tx object
         or an ID. The new state should be defined (see Transaction.Transaction)
         if it's not, the transaction will be deleted from the store '''       
     
     if not self.txStore:
         return
     
     #a little error checking:
     if new_state:
         try:
             new_state = int(new_state)
         except:
             shared.debug(0,["Critical error: we tried to update a transaction",\
                             "to a non-integer state! Quitting."])
             exit(1)
             
     tdbLock.acquire()
     try:    
         if not full:
             if not tx:
                 if not txID:
                     raise Exception("you called transactionUpdate without"+\
                                 "specifying a transaction! Doh!")
                 tx = self.getTxByID(txID)
             
             index = next((i for i in range(0,len(self.transactions)) \
                     if self.transactions[i].uniqID()==tx.uniqID()),None)
             shared.debug(0,["Set index to:",index,"for transaction:",tx.uniqID()])
             #bugfix 6 Oct; zero counts as false!!
             if index is None: #means this is a new transaction; add it
                 self.transactions.append(tx)
                 if not new_state:
                     raise Exception("You cannot add a transaction with no state!")
                 self.transactions[len(self.transactions)-1].state=new_state
             else:
                 if not new_state:
                     #get rid of it
                     del self.transactions[index]
                 else:
                     #update the state
                     shared.debug(0,["Updating transaction state to",new_state])
                     self.transactions[index].state = new_state
         
         #persist to file - note that persistence is definitely necessary,
         #but of course this primitive file-as-database would not really
         #be acceptable except for the fact that performance is not an issue.
         #TODO need to review whether system crash makes this unacceptable;
         #transfer to simple mysql database or something like that 
         with open(self.txFile,'w') as f:
             pickle.dump(self.transactions,f)
             shared.debug(0,["Dumped contents of transaction to file"])
     finally:
         tdbLock.release()
Exemple #45
0
    def transferTxToRE(self, tx, chosenEscrow):
        #send a message to the chosen escrow
        #this message contains the full transaction object
        transaction_string = pickle.dumps(tx)
        buyerSig = tx.contract.getSignature('buyer')
        sellerSig = tx.contract.getSignature('seller')
        txt, escrowSig = multisig.signText(
            self.uniqID(), tx.contract.getContractTextOrdered())
        m = 'CNE_RE_TRANSFER:'+'|'.join([transaction_string,buyerSig,\
                                         sellerSig,escrowSig,tx.depositHash])

        #Hack for testing: set chosen escrow to the "other"
        chosenEscrow = 1

        self.sendMessage(m,
                         recipientID='RE' +
                         self.escrowList[chosenEscrow]['id'],
                         txID=tx.uniqID())

        msg = self.getSingleMessage(20, prefix='CNE')

        if not msg:
            return False
        a, b = msg.keys()[0].split('.')
        if b != self.escrowList[chosenEscrow]['id'] or tx.uniqID() != a:
            #we have received an inappropriate message
            return False
        else:
            if 'RE_CNE_TX_CONFIRM_RECEIPT' in msg.values()[0]:
                #once transaction is confirmed received, we update its state so as not to send it again
                #this is the final state of the transaction on the contract negotiation side
                self.transactionUpdate(full=False,
                                       txID=tx.uniqID(),
                                       new_state=301)
                return True
            elif 'RE_CNE_TX_REJECT_RECEIPT' in msg.values()[0]:
                shared.debug(0, [
                    "Warning, transaction",
                    tx.uniqID(), "was not accepted by chosen Escrow",
                    self.escrowList[chosenEscrow]['id']
                ])
                #do nothing here; we need to send again
                #TODO what kind of rejection? parse "reason" field?
                return False
            else:
                raise Exception("Random escrow sent unrecognised message type")
Exemple #46
0
def get_ssl_hashes_from_capfile(capfile,
                                port=-1,
                                stream='',
                                options=[],
                                frames=[]):
    frames_str = ''
    ssl_frames = []
    #Run tshark to get a list of frames with ssl app data in them
    #EDITED to test escrow
    filterstr = build_filterstr(stream=stream, port=port)
    if (frames):
        ssl_frames = frames
    else:
        try:
            frames_str = tshark(capfile,field='frame.number', \
                            filter= filterstr,options=options)
        except:
            #this could be caused by a corrupt file from stcppipe
            #or by a malformed query string,etc. - but in the former
            #case we should NOT exit, hence this approach
            shared.debug(0, ["tshark failed - see stderr for message"])
            shared.debug(0, ["return code from tshark: ", frames_str])
            return None

        ssl_frames = shared.pisp(frames_str)
        #gracefully handle null result (i.e. blank tshark output):
        ssl_frames = filter(None, ssl_frames)
        if not ssl_frames:
            return None

    #Now we definitely have ssl frames in this capture file
    shared.debug(1, ['need to process this many frames:', len(ssl_frames)])
    ssl_app_data = tshark(capfile,field='ssl.app_data',frames=ssl_frames,\
                    options=options)
    #ssl.app_data will return all encrypted segments separated by commas
    #but also, lists of segments from different frames will be separated by
    #platform dependent newlines
    ssl_app_data_list = shared.pisp(ssl_app_data.replace(',', shared.PINL))
    #remove any blank OR duplicate entries in the ssl app data list
    ssl_app_data_list = filter(None, list(set(ssl_app_data_list)))
    shared.debug(4,
                 ["Full dump of ssl application data:\n", ssl_app_data_list])
    shared.debug(1,["Length of list of ssl segments for file ",capfile," was: " \
    ,len(ssl_app_data_list)])

    return get_ssl_hashes_from_ssl_app_data_list(ssl_app_data_list)
Exemple #47
0
def get_ssl_hashes_from_ssl_app_data_list(ssl_app_data_list):

    ssl_hashes = []
    for s in ssl_app_data_list:
        #get rid of commas and colons
        #(ssl.app_data comma-delimits multiple SSL segments within the same frame)
        s = s.rstrip()
        s = s.replace(',', ' ')
        s = s.replace(':', ' ')

        if s == '':
            shared.debug(
                2, ["Warning: empty ssl app data string passed for hashing!"])
        else:
            ssl_hashes.append(hashlib.md5(bytearray.fromhex(s)).hexdigest())

    return list(set(ssl_hashes))
Exemple #48
0
 def receiveContractCNE(self,msg):
     '''acting as CNE, the escrow can receive a doubly-signed
     contract at any time from any party. After verifying that the signatures are valid, and that the deposits 
     are specified correctly in the contract, the address
     for deposits is reported and the contract signed for the third time by the escrow.
     Messages sent to both parties giving them a deadline for deposit. '''       
     sender = msg[0].split('.')[1]
     #this special message is delimited by |
     allContractDetails = ':'.join(msg[1].split(':')[1:]).split('|')
     
     #the contract is in json format
     contractDetails = allContractDetails[0]
     tmpContract = Contract.Contract(json.loads(contractDetails))
     pubs = {}
     for s in allContractDetails[1:]:
         tmpPub = multisig.ecdsa_recover(tmpContract.getContractTextOrdered(),s)
         ad = multisig.pubtoaddr(tmpPub)
         shared.debug(2,["\n recovery produced this address: ",ad,"\n"])
         tmpContract.signatures[ad]=s
         #store the pubkeys for later use
         pubs[ad]= tmpPub
     
     #immediately check for 2 signatures; otherwise dump immediately
     if len(allContractDetails) != 3:
         return (False,'Not a valid and fully signed contract, ignoring',tmpContract)        
     
     #now the temporary contract object is fully populated; 
     #we can check the signatures match the IDs in the contract
     for k,v in tmpContract.signatures.iteritems():
         if k not in [tmpContract.text['Buyer BTC Address'],tmpContract.text['Seller BTC Address']]:
             shared.debug(1,['Error: signature',v,'from',k,'was invalid'])
             return (False,'Invalid contract signature',tmpContract)
     
     #need to check that the proposed deposits follow the business rules
     verdict,reason = self.checkBusinessRulesCNE(tmpContract)
     if not verdict: return (verdict, reason,tmpContract)
     
     #removed for now.
     #now we're happy that the contract is valid we build the dep multisig
     #multisig.initialise(g("Escrow","escrow_pubkey"),g("Directories","escrow_base_dir"))
     #for a,p in pubs.iteritems():
         #multisig.store_share(p,a)
     #msigaddr, mscript = multisig.create_multisig_address(*pubs.keys())
     
     return (True,multisig.pubtoaddr(g("Escrow","escrow_pubkey")),tmpContract)     
Exemple #49
0
 def createCollateralMultisig(self):
     #request to be adjudicator requires identity info
     #construct list of pubkeys
     escrowList = self.getEscrowList()
     pubkeyList=[x[2] for x in escrowList]
     mypub,mypriv = multisig.getKeysFromUniqueID(self.uniqID())
     pubkeyList.append(mypub)
     pubkeyList.sort()
     shared.debug(0,["Here are the pubkeys:",pubkeyList])
     #majority M of N total:
     N = len(pubkeyList)
     if N>15: #TODO magic number
         shared.debug(0,["Critical error: the total number of keys in this multisig address exceeds the maximum of 17 - aborting"])
         return None,None
     
     M = int(math.floor(N/2)+1)
     msigaddr,mscript = multisig.createMultisigRaw(M, N, pubkeyList)
     return (msigaddr,mscript)
Exemple #50
0
    def run(self, escrowRole='cne'):
        #the main loop to be called for the daemon meaning we're
        #listening for messages/requests/instructions from useragents.
        if escrowRole == 're':
            self.runRE()
            return
        elif escrowRole != 'cne':
            shared.debug(0, ["Error! Role must be cne or re"])
            exit(1)

        while True:

            #deal with transactions
            self.takeAppropriateActions()

            msg = self.getSingleMessage(5, prefix='CNE')
            if not msg:
                shared.debug(0, ["Got nothing, waiting.."])
                continue

            k, m = msg.items()[0]
            txID, requester = k.split('.')

            if 'ADJUDICATOR_APPLICATION:' in m:
                self.writeAdjudicatorApplication(':'.join(m.split(':')[1:]),
                                                 requester)
                continue

            if 'CNE_SIGNED_CONTRACT:' in m:
                verdict, data, contract = self.receiveContractCNE([k, m])
                self.sendContractVerdictCNE(verdict, data, contract)
                continue

            #TODO: this code is identical on RE and CNE;
            #how to avoid replicating?
            if 'QUERY_STATUS:' in m:
                queryee = m.split(':')[1]
                self.sendMessage('QUERY_STATUS:' + requester,
                                 recipientID=queryee)

            if 'QUERY_STATUS_RESPONSE:' in m:
                rspns, ctrprty = m.split(':')[1].split(',')
                self.sendMessage('QUERY_STATUS_RESPONSE:' + rspns,
                                 recipientID=ctrprty)
Exemple #51
0
    def startFirefox(self):
        #if not os.path.isdir(os.path.join(datadir, 'firefox')):
        #    os.mkdir(os.path.join(datadir, 'firefox'))
        ffdir = shared.makedir([self.baseDir, 'firefox'])
        #touch files
        for fn in ['stdout', 'stderr']:
            open(os.path.join(ffdir, 'firefox.' + fn), 'w').close()
        if not os.path.isfile(
                os.path.join(ffdir, 'FF-profile', 'extensions.ini')):
            #FF rewrites extensions.ini on first run, so we allow FF to create it,
            #then we kill FF, rewrite the file and start FF again
            try:
                self.ff_proc = subprocess.Popen([g("Exepaths","firefox_exepath"),\
                '-no-remote','-profile', os.path.join(ffdir, 'FF-profile')], \
                stdout=open(os.path.join(ffdir,"firefox.stdout"),'w'), \
                stderr=open(os.path.join(ffdir, "firefox.stderr"),'w'))

            except Exception, e:
                shared.debug(0, ["Error starting Firefox"])
                return ["Error starting Firefox"]

            while 1:
                time.sleep(0.5)
                if os.path.isfile(
                        os.path.join(ffdir, 'FF-profile', 'extensions.ini')):
                    self.ff_proc.kill()
                    break

            try:
                #enable extension
                with codecs.open (os.path.join(ffdir, 'FF-profile', \
                                               'extensions.ini'), "w") as f1:
                    f1.write("[ExtensionDirs]\nExtension0=" + \
                             os.path.join(ffdir, 'FF-profile',\
                             "extensions", "lspnr@lspnr") + "\n")
                #show addon bar
                with codecs.open(os.path.join(ffdir, \
                                'FF-profile', 'localstore.rdf'), 'w') as f2:
                    f2.write(
                        '<?xml version="1.0"?><RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#" xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><RDF:Description RDF:about="chrome://browser/content/browser.xul"><NC:persist RDF:resource="chrome://browser/content/browser.xul#addon-bar" collapsed="false"/></RDF:Description></RDF:RDF>'
                    )
            except Exception, e:
                shared.debug(0, ['File open error'])
                return False
Exemple #52
0
 def sendSignedContractToCtrprtyCNE(self):
     '''send a json dump of the contract contents
     also send to the chosen escrow if both parties signed        
     should already be initialised and signed'''
     if not self.workingContract or not self.workingContract.isSigned:
         return False
     msg_details = [self.workingContract.getContractText()]
     msg_details.extend([v for k,v in self.workingContract.signatures.iteritems()])
     msg = 'CNE_SIGNED_CONTRACT:'+'|'.join(msg_details)
     shared.debug(0,["sending message:",msg])
     if len(self.workingContract.signatures.keys())>1:
         shared.debug(0,["\n **Sending a complete contract to the escrow**\n"])
         self.persistContract(self.workingContract)
         self.sendMessage(msg,recipientID='CNE',\
                          txID=self.workingContract.textHash) 
     self.sendMessage(msg,\
         recipientID=self.workingContract.getCounterparty(self.uniqID()),\
         txID=self.workingContract.textHash)
     return True
Exemple #53
0
    def createCollateralMultisig(self):
        #request to be adjudicator requires identity info
        #construct list of pubkeys
        escrowList = self.getEscrowList()
        pubkeyList = [x[2] for x in escrowList]
        mypub, mypriv = multisig.getKeysFromUniqueID(self.uniqID())
        pubkeyList.append(mypub)
        pubkeyList.sort()
        shared.debug(0, ["Here are the pubkeys:", pubkeyList])
        #majority M of N total:
        N = len(pubkeyList)
        if N > 15:  #TODO magic number
            shared.debug(0, [
                "Critical error: the total number of keys in this multisig address exceeds the maximum of 17 - aborting"
            ])
            return None, None

        M = int(math.floor(N / 2) + 1)
        msigaddr, mscript = multisig.createMultisigRaw(M, N, pubkeyList)
        return (msigaddr, mscript)
Exemple #54
0
    def payInitialFees(self):
        #Working in mbtc here.
        txf = shared.defaultBtcTxFee
        if not self.workingContract:
            raise Exception("Tried to pay fees but there's no active contract")

        btc = int(self.workingContract.text['mBTC Amount'])
        bd = int(self.workingContract.text['Buyer Deposit Fee'])
        sd = int(self.workingContract.text['Seller Deposit Fee'])
        bf = int(self.workingContract.text['Buyer Escrow Fee'])
        sf = int(self.workingContract.text['Seller Escrow Fee'])

        #get our role
        role = 'buyer' if self.workingContract.text[
            'Buyer BTC Address'] == self.uniqID() else 'seller'

        if role == 'buyer':
            feeToPay = bd + bf
        else:
            #TODO is it either feasible or desirable for the seller to pay 'btc' here? Probably not.
            feeToPay = sd + sf

        #check that we hold sufficient funds; values are returned in btc
        c, u = multisig.get_balance_lspnr(self.uniqID())
        #the extra 0.5 fee to be paid is to allow for cost of transfer from cne to re
        if feeToPay + math.ceil(txf * 1.5) >= c:
            shared.debug(0,["Cannot pay - insufficient funds. Confirmed funds:",\
                            str(c),",unconfirmed:",str(u),"while fee is: ",str(feeToPay+math.ceil(txf*1.5))])
            return

        #we use any utxos, so "payers" is None
        feeSpendingTxHash = multisig.spendUtxos(self.uniqID(),self.uniqID(),\
                            multisig.pubtoaddr(g("Escrow","escrow_pubkey")),None,amt=feeToPay+txf)
        if not feeSpendingTxHash:
            shared.debug(0, [
                "Error, cannot pay the fee because of insufficient funds at address:",
                myBtcAddress
            ])
            return False
        else:
            return feeSpendingTxHash
Exemple #55
0
 def run(self,escrowRole='cne'):
     #the main loop to be called for the daemon meaning we're
     #listening for messages/requests/instructions from useragents.
     if escrowRole=='re':
         self.runRE()
         return
     elif escrowRole != 'cne':
         shared.debug(0,["Error! Role must be cne or re"])
         exit(1)
     
     while True:
         
         #deal with transactions
         self.takeAppropriateActions() 
         
         msg = self.getSingleMessage(5,prefix='CNE')
         if not msg:
             shared.debug(0,["Got nothing, waiting.."])
             continue
         
         k,m = msg.items()[0]
         txID, requester = k.split('.')
         
         if 'ADJUDICATOR_APPLICATION:' in m:
             self.writeAdjudicatorApplication(':'.join(m.split(':')[1:]),requester)
             continue
         
         if 'CNE_SIGNED_CONTRACT:' in m:
             verdict,data,contract = self.receiveContractCNE([k,m])
             self.sendContractVerdictCNE(verdict,data,contract)
             continue
         
         #TODO: this code is identical on RE and CNE;
         #how to avoid replicating?
         if 'QUERY_STATUS:' in m:
             queryee = m.split(':')[1]
             self.sendMessage('QUERY_STATUS:'+requester, recipientID=queryee) 
         
         if 'QUERY_STATUS_RESPONSE:' in m:
             rspns,ctrprty = m.split(':')[1].split(',')
             self.sendMessage('QUERY_STATUS_RESPONSE:'+rspns, recipientID=ctrprty)
Exemple #56
0
def get_ssl_hashes_from_capfile(capfile,port=-1,stream='',options=[],frames=[]):
    frames_str=''
    ssl_frames=[]
    #Run tshark to get a list of frames with ssl app data in them
    #EDITED to test escrow
    filterstr = build_filterstr(stream=stream,port=port)
    if (frames):
        ssl_frames=frames
    else:
        try:
            frames_str = tshark(capfile,field='frame.number', \
                            filter= filterstr,options=options)
        except:
            #this could be caused by a corrupt file from stcppipe
            #or by a malformed query string,etc. - but in the former
            #case we should NOT exit, hence this approach
            shared.debug(0,["tshark failed - see stderr for message"])
            shared.debug(0,["return code from tshark: ",frames_str])
            return None
    
        ssl_frames = shared.pisp(frames_str)
        #gracefully handle null result (i.e. blank tshark output):
        ssl_frames = filter(None,ssl_frames)
        if not ssl_frames:
            return None
    
    #Now we definitely have ssl frames in this capture file
    shared.debug(1,['need to process this many frames:', len(ssl_frames)])
    ssl_app_data = tshark(capfile,field='ssl.app_data',frames=ssl_frames,\
                    options=options)
    #ssl.app_data will return all encrypted segments separated by commas
    #but also, lists of segments from different frames will be separated by
    #platform dependent newlines
    ssl_app_data_list = shared.pisp(ssl_app_data.replace(',',shared.PINL))
    #remove any blank OR duplicate entries in the ssl app data list
    ssl_app_data_list = filter(None,list(set(ssl_app_data_list)))
    shared.debug(4,["Full dump of ssl application data:\n",ssl_app_data_list])
    shared.debug(1,["Length of list of ssl segments for file ",capfile," was: " \
    ,len(ssl_app_data_list)])
    
    return get_ssl_hashes_from_ssl_app_data_list(ssl_app_data_list)
Exemple #57
0
 def getSingleMessage(self,timeout=1,chanIndex=0,prefix=None,external=False):
     qname = prefix+self.uniqID() if prefix else self.uniqID()
     msg = Msg.getSingleMessage(qname,timeout,chanIndex,external)
     if not msg:
         shared.debug(5,["Message layer returned none"])
         return None
     #all messages must be verified
     shared.logToFile(g("Directories","agent_base_dir"),"Signed message received:"+msg.keys()[0]+':'+msg.values()[0]+ " at time:"+str(time.time()))
     sendingID = msg.keys()[0].split('.')[1]
     #retrieve pubkey
     msgInner = ';'.join(':'.join(msg.values()[0].split(':')[1:]).split(';')[:-1])
     sig = ':'.join(msg.values()[0].split(':')[1:]).split(';')[-1]
     addr = multisig.pubtoaddr(multisig.ecdsa_recover(msgInner,sig))
     
     if not addr == sendingID:
         #don't add anything but failure to message to prevent leaks
         shared.debug(0,["Verification failure",sendingID,chanIndex])
         self.sendMessage({msg.keys()[0]:'VERIFICATION_FAILED:'},sendingID,chanIndex)
         return None
     else:
         #having checked the message signature, dispose of it
         v = ';'.join(msg.values()[0].split(';')[:-1])
         msg = {msg.keys()[0]:v}
         shared.debug(4,["Returning this message:",msg])
         return msg    
Exemple #58
0
 def startFirefox(self):    
     #if not os.path.isdir(os.path.join(datadir, 'firefox')): 
     #    os.mkdir(os.path.join(datadir, 'firefox'))
     ffdir = shared.makedir([self.baseDir,'firefox'])
     #touch files
     for fn in ['stdout','stderr']:
         open(os.path.join(ffdir,'firefox.'+fn), 'w').close()
     if not os.path.isfile(os.path.join(ffdir,'FF-profile', 'extensions.ini')):
     #FF rewrites extensions.ini on first run, so we allow FF to create it,
     #then we kill FF, rewrite the file and start FF again
         try:
             self.ff_proc = subprocess.Popen([g("Exepaths","firefox_exepath"),\
             '-no-remote','-profile', os.path.join(ffdir, 'FF-profile')], \
             stdout=open(os.path.join(ffdir,"firefox.stdout"),'w'), \
             stderr=open(os.path.join(ffdir, "firefox.stderr"),'w'))
             
         except Exception,e:
             shared.debug(0,["Error starting Firefox"])
             return ["Error starting Firefox"]
         
         while 1:
             time.sleep(0.5)
             if os.path.isfile(os.path.join(ffdir,'FF-profile', 'extensions.ini')):
                 self.ff_proc.kill()
                 break
             
         try:
             #enable extension                            
             with codecs.open (os.path.join(ffdir, 'FF-profile', \
                                            'extensions.ini'), "w") as f1:
                 f1.write("[ExtensionDirs]\nExtension0=" + \
                          os.path.join(ffdir, 'FF-profile',\
                          "extensions", "lspnr@lspnr") + "\n")
             #show addon bar
             with codecs.open(os.path.join(ffdir, \
                             'FF-profile', 'localstore.rdf'), 'w') as f2:
                 f2.write('<?xml version="1.0"?><RDF:RDF xmlns:NC="http://home.netscape.com/NC-rdf#" xmlns:RDF="http://www.w3.org/1999/02/22-rdf-syntax-ns#"><RDF:Description RDF:about="chrome://browser/content/browser.xul"><NC:persist RDF:resource="chrome://browser/content/browser.xul#addon-bar" collapsed="false"/></RDF:Description></RDF:RDF>')    
         except Exception,e:
             shared.debug(0,['File open error'])
             return False