def sign_tx(self, msg):
        '''
        This function starts workflow of signing Bitcoin transaction.
        Function set up the environment and send back a input request message,
        asking computer for first input.
        '''
        self.set_main_state()

        if msg.inputs_count < 1:
            return proto.Failure(
                message='Transaction must have at least one input')

        if msg.outputs_count < 1:
            return proto.Failure(
                message='Transaction must have at least one output')

        if msg.random == '':
            return proto.Failure(message='No random data received')

        self.inputs_count = msg.inputs_count
        self.outputs_count = msg.outputs_count
        self.random = msg.random

        return proto.TxRequest(request_type=proto.TXINPUT,
                               request_index=self.input_index)
 def _check_address_n(self, msg):
     if len(msg.address_n):
         # Recalculate output address and compare with msg.address
         if msg.address != self.wallet.get_address(msg.address_n):
             self.set_main_state()
             return proto.Failure(
                 message="address_n doesn't belong to given bitcoin address"
             )
Beispiel #3
0
 def process_debug_message(self, msg):
     # Process messages handled by debugging connection
     try:
         return self._process_debug_message(msg)
     except Exception as exc:
         traceback.print_exc()
         self.set_main_state()
         return proto.Failure(message=str(exc))
Beispiel #4
0
 def otp_check(self, otp):
     if otp == self.otp:
         msg = self.otp_func(*self.otp_args)
         self.otp_cancel()
         return msg
     else:
         time.sleep(3)
         self.otp_cancel()
         return proto.Failure(code=3, message="Invalid OTP")
Beispiel #5
0
    def _process_debug_message(self, msg):
        if isinstance(msg, proto.DebugLinkGetState):
            # Report device state                
            return self.debug_get_state(msg)
        
        if isinstance(msg, proto.LoadDevice):
            return self.debug_load_wallet(msg.algo, msg.seed, msg.otp, msg.pin, msg.spv)

        self.set_main_state()
        return proto.Failure(code=1, message="Unexpected message")
    def tx_input(self, msg):
        '''
        This message is called on tx input message.
        '''

        if msg.index != self.input_index:
            self.set_main_state()
            return proto.Failure(
                message="Input index doesn't correspond with internal state")

        print "RECEIVED INPUT", msg

        if msg.index == self.signing_index:
            # Store message to cache for serializing input in tx_output
            self.signing_input = msg
        '''
        There we have received one input.
        '''
        if self.signing_index == 0:
            '''
            If it is first one, we have to prepare
            and hash the beginning of the transaction.
            '''

            if self.input_index == 0:
                # First input, let's hash the beginning of tx
                self.input_hash = hashlib.sha256(
                    signing.raw_tx_header(self.inputs_count))
                self.tx_hash = self.input_hash

            #self.tx_hash.update()

        #self.input_hash.update(signing.raw_tx_input())
        #self.tx_hash.update(signing.raw_tx_input())
        # TODO
        '''
        For every input, hash the input itself.
        '''
        print "INPUT HASH", self.input_hash.hexdigest()
        # TODO

        if self.input_index < self.inputs_count - 1:
            '''
            If this is not the last input, request next input in the row.
            '''
            self.input_index += 1
            return proto.TxRequest(request_type=proto.TXINPUT,
                                   request_index=self.input_index)
        '''
        We have processed all inputs. Let's request transaction outputs now.
        '''
        self.output_index = 0
        self.output_hash = hashlib.sha256()
        return proto.TxRequest(request_type=proto.TXOUTPUT,
                               request_index=self.output_index)
Beispiel #7
0
 def process_message(self, msg):
     # Any exception thrown during message processing
     # will result in Failure message instead of application crash
     try:
         ret = self._process_message(msg)
         if isinstance(ret, proto.Failure):
             self.set_main_state()
         return ret
     except Exception as exc:
         traceback.print_exc()
         self.set_main_state()
         return proto.Failure(message=str(exc))
Beispiel #8
0
 def check(self, otp):
     # OTP is displayed with spaces, but they aren't significant
     otp = otp.replace(' ', '')
     
     if otp == self.otp:
         msg = self.func(*self.args)
         self.cancel()
         return msg
     else:
         time.sleep(3)
         self.cancel()
         self.set_main_state()
         return proto.Failure(code=3, message="Invalid OTP")
Beispiel #9
0
 def pin_check(self, pin):
     if self.pin_pass_or_check:
         # Pass PIN to method
         msg = self.pin_func(pin, *self.pin_args)
         self.pin_cancel()
         return msg
     else:
         # Check PIN against device's internal PIN
         if pin == self.device.pin:
             msg = self.pin_func(*self.pin_args)
             self.pin_cancel()
             return msg
         else:
             time.sleep(3)
             self.pin_cancel()
             return proto.Failure(code=6, message="Invalid PIN")
Beispiel #10
0
 def check(self, pin):
     if self.pass_or_check:
         # Pass PIN to method
         msg = self.func(pin, *self.args)
         self.cancel()
         return msg
     else:
         # Check PIN against device's internal PIN
         if pin == self.wallet.struct.pin:
             msg = self.func(*self.args)
             self.cancel()
             return msg
         else:
             time.sleep(3)
             self.cancel()
             self.set_main_state()
             return proto.Failure(code=6, message="Invalid PIN")
Beispiel #11
0
    def protect_call(self, yesno_message, otp_message, pin_message, func,
                     *args):
        if not self.yesno(yesno_message):
            return proto.Failure(code=4, message='Action cancelled by user')

        if self.device.otp:
            if self.device.pin:
                return self.otp_request(
                    otp_message, self.pin_request,
                    *[pin_message, False, func] + list(args))
            else:
                return self.otp_request(otp_message, func, *args)

        if self.device.pin:
            return self.pin_request(pin_message, False, func, *args)

        return func(*args)
    def process_message(self, msg):
        if isinstance(msg, proto.SignTx):
            # Start signing process
            return self.sign_tx(msg)

        if isinstance(msg, proto.TxInput):
            return self.tx_input(msg)

        if isinstance(msg, proto.TxOutput):
            if self.ser_output:
                # We just want to serialize part of output
                # and send it back to computer
                return self.serialize_output(msg)
            else:
                return self.tx_output(msg)

        # return Failure message to indicate problems to upstream SM
        return proto.Failure(code=1, message="Signing failed")
Beispiel #13
0
 def resolve(self):
     if not self.func:
         # We're not waiting for hw buttons
         return
     
     if self.pending:
         # We still didn't received ButtonAck from computer
         return
     
     if self.decision == None:
         # We still don't know user's decision (call yesno_store() firstly)
         return
     
     if self.decision == True:
         ret = self.func(*self.args)
     else:
         self.set_main_state()
         ret = proto.Failure(code=4, message='Action cancelled by user')
         
     self.func = None
     self.args = []
     return ret
 def _check_output_index(self, msg):
     if msg.index != self.output_index:
         self.set_main_state()
         return proto.Failure(
             message="Output index doesn't correspond with internal state")
Beispiel #15
0
    def _process_message(self, msg):
        if isinstance(msg, proto.Initialize):
            self.set_main_state()
            m = self.wallet.get_features()
            m.session_id = msg.session_id
            return m
        
        if self.otp.is_waiting():
            '''OTP response is expected'''
            if isinstance(msg, proto.OtpAck):
                return self.otp.check(msg.otp)
        
            if isinstance(msg, proto.OtpCancel):
                self.otp.cancel()
                return proto.Success(message="OTP cancelled")
            
            self.set_main_state()
            return proto.Failure(code=2, message='OTP expected')
            
        if self.pin.is_waiting():            
            '''PIN response is expected'''
            if isinstance(msg, proto.PinAck):
                return self.pin.check(msg.pin)
            
            if isinstance(msg, proto.PinCancel):
                self.pin_cancel()
                return proto.Success(message="PIN request cancelled")
            
            self.set_main_state()
            return proto.Failure(code=5, message='PIN expected')
        
        if self.yesno.is_waiting():
            '''Button confirmation is expected'''
            if isinstance(msg, proto.ButtonAck):
                self.yesno.allow()
                return self.yesno.resolve() # Process if button has been already pressed
            
            if isinstance(msg, proto.ButtonCancel):
                self.set_main_state()
                return proto.Success(message="Button confirmation cancelled")
        
            self.set_main_state()
            return proto.Failure(code=2, message='Button confirmation expected')
            
        if isinstance(msg, proto.Ping):
            return proto.Success(message=msg.message)
                
        if isinstance(msg, proto.GetUUID):
            return proto.UUID(UUID=self.wallet.get_UUID())
                
        if isinstance(msg, proto.GetEntropy):
            return self.protect_call(["Send %d bytes" % msg.size, "of entropy", "to computer?"],
                                      '', '{ Cancel', 'Confirm }',
                                     None, None,
                                     self._get_entropy, msg.size)

        if isinstance(msg, proto.GetMasterPublicKey):
            return proto.MasterPublicKey(key=self.wallet.get_master_public_key())
    
        if isinstance(msg, proto.GetAddress):
            address = self.wallet.get_address(list(msg.address_n))
            self.layout.show_receiving_address(address)
            self.custom_message = True # Yes button will redraw screen
            return proto.Address(address=address)
    
        if isinstance(msg, proto.SetMaxFeeKb):
            return self.protect_call(["Current maximal fee",
                                     "is %s per kB." % self.wallet.maxfee_kb,
                                     "Set transaction fee",
                                     "to %s per kB?" % msg.maxfee_kb],
                                     '', '{ Cancel', 'Confirm }',
                                     None, None,
                                     self._set_maxfee_kb, msg.maxfee_kb)    
                        
        if isinstance(msg, proto.ResetDevice):
            return self.protect_reset("Reset device?",
                                     '', '{ Cancel', 'Confirm }', None,
                                     msg.random)
            
        if isinstance(msg, (proto.SignTx, proto.TxInput, proto.TxOutput)):
            return self.signing.process_message(msg)
                    
        self.set_main_state()
        return proto.Failure(code=1, message="Unexpected message")
Beispiel #16
0
    def process_message(self, msg):
        if isinstance(msg, proto.Initialize):
            self.otp_cancel()
            self.pin_cancel()

            m = proto.Features()
            m.session_id = msg.session_id
            m.vendor = self.device.vendor
            m.major_version = self.device.major_version
            m.minor_version = self.device.minor_version
            m.otp = self.device.otp == True
            m.pin = self.device.pin != ''
            m.spv = self.device.spv == True
            m.algo.extend(self.device.algo)
            m.maxfee_kb = self.device.maxfee_kb
            m.debug_link = self.device.debug_link
            return m

        if self.otp != None:
            '''OTP response is expected'''

            if isinstance(msg, proto.OtpAck):
                return self.otp_check(msg.otp)

            if isinstance(msg, proto.OtpCancel):
                self.otp_cancel()
                return proto.Success(message="OTP cancelled")

            return proto.Failure(code=2, message='Waiting for OTP')

        if self.pin_func != None:
            '''PIN response is expected'''
            if isinstance(msg, proto.PinAck):
                return self.pin_check(msg.pin)

            if isinstance(msg, proto.PinCancel):
                self.pin_cancel()
                return proto.Success(message="PIN request cancelled")

            return proto.Failure(code=5, message='Waiting for PIN')

        if isinstance(msg, proto.Ping):
            return proto.Success(message=msg.message)

        if isinstance(msg, proto.GetUUID):
            return proto.UUID(UUID='device-UUID')

        if isinstance(msg, proto.GetEntropy):
            return self.protect_call(
                "Send %d bytes of entropy to computer?" % msg.size, None, None,
                self._get_entropy, msg.size)

        if isinstance(msg, proto.GetMasterPublicKey):
            return proto.MasterPublicKey(
                key=self.device.get_master_public_key(msg.algo))

        if isinstance(msg, proto.SetMaxFeeKb):
            return self.protect_call("Current maximal fee is %s per kB. Set transaction fee to %s per kilobyte?" % \
                                     (self.device.maxfee_kb, msg.maxfee_kb),
                                     None, None,
                                     self._set_maxfee_kb, msg.maxfee_kb)

        if isinstance(msg, proto.LoadDevice):
            return self.protect_call("Load device with custom seed?", None,
                                     None, self._load_device, msg.seed,
                                     msg.otp, msg.pin, msg.spv)

        if isinstance(msg, proto.ResetDevice):
            return self.protect_call("Reset device?", None, None,
                                     self._reset_device, msg.random)

        if isinstance(msg, proto.SignTx):
            print "<TODO: Print transaction details>"
            return self.protect_call("Sign transaction?", None, None,
                                     self._sign_tx, msg)

        return proto.Failure(code=1, message='Unknown method')