rpc_signer.py raw

   1  #!/usr/bin/env python3
   2  # Copyright (c) 2017-2022 The Limenka developers
   3  # Distributed under the MIT software license, see the accompanying
   4  # file COPYING or http://www.opensource.org/licenses/mit-license.php.
   5  """Test external signer.
   6  
   7  Verify that a limenkad node can use an external signer command.
   8  See also wallet_signer.py for tests that require wallet context.
   9  """
  10  import os
  11  import platform
  12  import sys
  13  
  14  from test_framework.test_framework import LimenkaTestFramework
  15  from test_framework.util import (
  16      assert_equal,
  17      assert_raises_rpc_error,
  18  )
  19  
  20  
  21  class RPCSignerTest(LimenkaTestFramework):
  22      def mock_signer_path(self):
  23          path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'mocks', 'signer.py')
  24          return sys.executable + " " + path
  25  
  26      def set_test_params(self):
  27          self.num_nodes = 4
  28  
  29          self.extra_args = [
  30              [],
  31              [f"-signer={self.mock_signer_path()}", '-keypool=10'],
  32              [f"-signer={self.mock_signer_path()}", '-keypool=10'],
  33              ["-signer=fake.py"],
  34          ]
  35  
  36      def skip_test_if_missing_module(self):
  37          self.skip_if_no_external_signer()
  38  
  39      def set_mock_result(self, node, res):
  40          with open(os.path.join(node.cwd, "mock_result"), "w", encoding="utf8") as f:
  41              f.write(res)
  42  
  43      def clear_mock_result(self, node):
  44          os.remove(os.path.join(node.cwd, "mock_result"))
  45  
  46      def run_test(self):
  47          self.log.debug(f"-signer={self.mock_signer_path()}")
  48  
  49          assert_raises_rpc_error(-1, 'Error: restart limenkad with -signer=<cmd>',
  50              self.nodes[0].enumeratesigners
  51          )
  52  
  53          # Handle script missing:
  54          assert_raises_rpc_error(
  55              -1,
  56              "CreateProcess failed: The system cannot find the file specified."
  57              if platform.system() == "Windows"
  58              else "execve failed: No such file or directory",
  59              self.nodes[3].enumeratesigners,
  60          )
  61  
  62          # Handle error thrown by script
  63          self.set_mock_result(self.nodes[1], "2")
  64          assert_raises_rpc_error(-1, 'RunCommandParseJSON error',
  65              self.nodes[1].enumeratesigners
  66          )
  67          self.clear_mock_result(self.nodes[1])
  68  
  69          self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "error": "fingerprint not found"}]')
  70          assert_raises_rpc_error(-1, 'fingerprint not found',
  71              self.nodes[1].enumeratesigners
  72          )
  73          self.clear_mock_result(self.nodes[1])
  74  
  75          # Malicious fingerprint with shell metacharacters must be rejected
  76          self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "fingerprint": "00000001; rm -rf /"}]')
  77          assert_raises_rpc_error(-1, 'received invalid fingerprint',
  78              self.nodes[1].enumeratesigners
  79          )
  80          self.clear_mock_result(self.nodes[1])
  81  
  82          # Empty fingerprint must be rejected
  83          self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "fingerprint": ""}]')
  84          assert_raises_rpc_error(-1, 'received invalid fingerprint',
  85              self.nodes[1].enumeratesigners
  86          )
  87          self.clear_mock_result(self.nodes[1])
  88  
  89          # Too-short hex fingerprint must be rejected (7 chars)
  90          self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "fingerprint": "0000001"}]')
  91          assert_raises_rpc_error(-1, 'received invalid fingerprint',
  92              self.nodes[1].enumeratesigners
  93          )
  94          self.clear_mock_result(self.nodes[1])
  95  
  96          # Too-long hex fingerprint must be rejected (9 chars)
  97          self.set_mock_result(self.nodes[1], '0 [{"type": "trezor", "model": "trezor_t", "fingerprint": "000000001"}]')
  98          assert_raises_rpc_error(-1, 'received invalid fingerprint',
  99              self.nodes[1].enumeratesigners
 100          )
 101          self.clear_mock_result(self.nodes[1])
 102  
 103          assert_equal({'fingerprint': '00000001', 'name': 'trezor_t'} in self.nodes[1].enumeratesigners()['signers'], True)
 104  
 105  if __name__ == '__main__':
 106      RPCSignerTest(__file__).main()
 107