Coverage for tests/admin/test_changepin.py: 100%
82 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-07-10 13:43 +0000
1# The MIT License (MIT)
2#
3# Copyright (c) 2021 RSK Labs Ltd
4#
5# Permission is hereby granted, free of charge, to any person obtaining a copy of
6# this software and associated documentation files (the "Software"), to deal in
7# the Software without restriction, including without limitation the rights to
8# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
9# of the Software, and to permit persons to whom the Software is furnished to do
10# so, subject to the following conditions:
11#
12# The above copyright notice and this permission notice shall be included in all
13# copies or substantial portions of the Software.
14#
15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21# SOFTWARE.
23from types import SimpleNamespace
24from unittest import TestCase
25from unittest.mock import Mock, call, patch
26from admin.changepin import do_changepin
27from admin.misc import AdminError
28from comm.platform import Platform
29from ledger.hsm2dongle import HSM2Dongle
31import logging
33logging.disable(logging.CRITICAL)
36@patch("sys.stdout.write")
37@patch("admin.changepin.get_hsm")
38class TestChangepin(TestCase):
39 VALID_PIN = '1234ABCD'
40 INVALID_PIN = '123456789'
42 def setUp(self):
43 options = {
44 'new_pin': self.VALID_PIN,
45 'no_unlock': False,
46 'any_pin': False,
47 'verbose': False,
48 'pin': self.VALID_PIN
49 }
50 self.default_options = SimpleNamespace(**options)
51 self.dongle = Mock()
52 Platform.set(Platform.LEDGER)
54 @patch("admin.changepin.do_unlock")
55 def test_changepin(self, do_unlock_mock, get_hsm, _):
56 get_hsm.return_value = self.dongle
57 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.BOOTLOADER)
58 self.dongle.is_onboarded = Mock(return_value=True)
59 self.dongle.new_pin = Mock(return_value=True)
61 do_changepin(self.default_options)
63 self.assertTrue(do_unlock_mock.called)
64 self.assertTrue(self.dongle.new_pin.called)
65 self.assertEqual([call(self.VALID_PIN.encode())],
66 self.dongle.new_pin.call_args_list)
68 @patch("admin.changepin.do_unlock")
69 def test_changepin_unlock_error(self, do_unlock_mock, get_hsm, _):
70 get_hsm.return_value = self.dongle
71 do_unlock_mock.side_effect = Exception('unlock-error')
72 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.BOOTLOADER)
73 self.dongle.is_onboarded = Mock(return_value=True)
74 self.dongle.new_pin = Mock(return_value=True)
76 with self.assertRaises(AdminError) as e:
77 do_changepin(self.default_options)
79 self.assertEqual('Failed to unlock device: unlock-error', str(e.exception))
81 @patch("admin.changepin.do_unlock")
82 def test_changepin_invalid_mode_ledger(self, do_unlock_mock, get_hsm, _):
83 get_hsm.return_value = self.dongle
84 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.SIGNER)
85 self.dongle.is_onboarded = Mock(return_value=True)
86 self.dongle.new_pin = Mock(return_value=True)
88 with self.assertRaises(AdminError) as e:
89 do_changepin(self.default_options)
91 self.assertTrue(do_unlock_mock.called)
92 self.assertTrue(str(e.exception).startswith('Device not in bootloader mode.'))
93 self.assertFalse(self.dongle.new_pin.called)
95 @patch("admin.changepin.do_unlock")
96 def test_changepin_signer_mode_sgx(self, do_unlock_mock, get_hsm, _):
97 Platform.set(Platform.SGX)
98 get_hsm.return_value = self.dongle
99 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.SIGNER)
100 self.dongle.is_onboarded = Mock(return_value=True)
101 self.dongle.new_pin = Mock(return_value=True)
103 do_changepin(self.default_options)
105 self.assertTrue(do_unlock_mock.called)
106 self.assertTrue(self.dongle.new_pin.called)
107 self.assertEqual([call(self.VALID_PIN.encode())],
108 self.dongle.new_pin.call_args_list)
110 def test_changepin_invalid_pin(self, get_hsm, _):
111 get_hsm.return_value = self.dongle
113 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.BOOTLOADER)
114 self.dongle.is_onboarded = Mock(return_value=True)
115 self.dongle.new_pin = Mock(return_value=True)
117 options = self.default_options
118 options.new_pin = self.INVALID_PIN
119 with self.assertRaises(AdminError) as e:
120 do_changepin(options)
122 self.assertTrue(str(e.exception).startswith('Invalid pin given.'))
123 self.assertFalse(self.dongle.new_pin.called)
125 @patch("admin.changepin.do_unlock")
126 def test_changepin_newpin_error(self, do_unlock_mock, get_hsm, _):
127 get_hsm.return_value = self.dongle
128 self.dongle.get_current_mode = Mock(return_value=HSM2Dongle.MODE.BOOTLOADER)
129 self.dongle.is_onboarded = Mock(return_value=True)
130 self.dongle.new_pin = Mock(return_value=False)
132 with self.assertRaises(AdminError) as e:
133 do_changepin(self.default_options)
135 self.assertTrue(do_unlock_mock.called)
136 self.assertEqual('Failed to change pin', str(e.exception))