From ca14d1a995315fc30efa387a1923ad229e485f93 Mon Sep 17 00:00:00 2001 From: slush0 Date: Fri, 21 Feb 2014 02:33:23 +0100 Subject: [PATCH] Renamed unit tests for better consistency --- tests/test_messages.py | 83 --------------- tests/test_msg_getentropy.py | 31 ++++++ tests/test_msg_loaddevice.py | 68 ++++++++++++ tests/test_msg_recoverydevice.py | 12 +++ tests/test_msg_resetdevice.py | 175 +++++++++++++++++++++++++++++++ tests/test_msg_wipedevice.py | 25 +++++ 6 files changed, 311 insertions(+), 83 deletions(-) delete mode 100644 tests/test_messages.py create mode 100644 tests/test_msg_getentropy.py create mode 100644 tests/test_msg_loaddevice.py create mode 100644 tests/test_msg_recoverydevice.py create mode 100644 tests/test_msg_resetdevice.py create mode 100644 tests/test_msg_wipedevice.py diff --git a/tests/test_messages.py b/tests/test_messages.py deleted file mode 100644 index 0104f0a..0000000 --- a/tests/test_messages.py +++ /dev/null @@ -1,83 +0,0 @@ -import unittest -import common -import binascii -import base64 - -from trezorlib.client import CallException - -class TestMessages(common.TrezorTest): - - def test_message_sign(self): - self.setup_mnemonic_nopin_nopassphrase() - sig = self.client.sign_message([0], "This is an example of a signed message.") - self.assertEqual(sig.address, '14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e') - self.assertEqual(binascii.hexlify(sig.signature), '209e23edf0e4e47ff1dec27f32cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80') - - def test_too_long(self): - self.setup_mnemonic_nopin_nopassphrase() - - # Message cannot be longer than 255 bytes - self.assertRaises(CallException, self.client.sign_message, [0], '1' * 256) - - ret = self.client.verify_message('1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T', - binascii.unhexlify('1ba77e01a9e17ba158b962cfef5f13dfed676ffc2b4bada24e58f784458b52b97421470d001d53d5880cf5e10e76f02be3e80bf21e18398cbd41e8c3b4af74c8c2'), - '1' * 256 - ) - self.assertFalse(ret) - - def test_message_testnet(self): - sig = base64.b64decode('IFP/nvQalDo9lWCI7kScOzRkz/fiiScdkw7tFAKPoGbl6S8AY3wEws43s2gR57AfwZP8/8y7+F+wvGK9phQghN4=') - ret = self.client.verify_message('moRDikgmxcpouFtqnKnVVzLYgkDD2gQ3sk', sig, 'Ahoj') - - self.assertTrue(ret) - - def test_message_verify(self): - # uncompressed pubkey - OK - res = self.client.verify_message( - '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T', - binascii.unhexlify('1ba77e01a9e17ba158b962cfef5f13dfed676ffc2b4bada24e58f784458b52b97421470d001d53d5880cf5e10e76f02be3e80bf21e18398cbd41e8c3b4af74c8c2'), - 'This is an example of a signed message.' - ) - self.assertTrue(res) - - # uncompressed pubkey - FAIL - res = self.client.verify_message( - '1JwSSubhmg6iPtRjtyqhUYYH7bZg3Lfy1T', - binascii.unhexlify('1ba77e01a9e17ba158b96200000000dfed676ffc2b4bada24e58f784458b52b97421470d001d53d5880cf5e10e76f02be3e80bf21e18398cbd41e8c3b4af74c8c2'), - 'This is an example of a signed message.' - ) - self.assertFalse(res) - - # compressed pubkey - OK - res = self.client.verify_message( - '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8', - binascii.unhexlify('1f44e3e461f7ca9f57c472ce1a28214df1de1dadefb6551a32d1907b80c74d5a1fbfd6daaba12dd8cb06699ce3f6941fbe0f3957b5802d13076181046e741eaaaf'), - 'This is an example of a signed message.') - self.assertTrue(res) - - # compressed pubkey - FAIL - res = self.client.verify_message( - '1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8', - binascii.unhexlify('1f44e3e461f7ca9f57c472000000004df1de1dadefb6551a32d1907b80c74d5a1fbfd6daaba12dd8cb06699ce3f6941fbe0f3957b5802d13076181046e741eaaaf'), - 'This is an example of a signed message.' - ) - self.assertFalse(res) - - # trezor pubkey - OK - res = self.client.verify_message( - '14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e', - binascii.unhexlify('209e23edf0e4e47ff1dec27f32cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80'), - 'This is an example of a signed message.' - ) - self.assertTrue(res) - - # trezor pubkey - FAIL - res = self.client.verify_message( - '14LmW5k4ssUrtbAB4255zdqv3b4w1TuX9e', - binascii.unhexlify('209e23edf0e4e47ff1de000002cd78c50e74ef018ee8a6adf35ae17c7a9b0dd96f48b493fd7dbab03efb6f439c6383c9523b3bbc5f1a7d158a6af90ab154e9be80'), - 'This is an example of a signed message.' - ) - self.assertFalse(res) - -if __name__ == '__main__': - unittest.main() diff --git a/tests/test_msg_getentropy.py b/tests/test_msg_getentropy.py new file mode 100644 index 0000000..831a70b --- /dev/null +++ b/tests/test_msg_getentropy.py @@ -0,0 +1,31 @@ +import unittest +import common +import math + +import trezorlib.messages_pb2 as proto +import trezorlib.types_pb2 as proto_types + +def entropy(data): + counts = {} + for c in data: + if c in counts: + counts[c] += 1 + else: + counts[c] = 1 + e = 0 + for _, v in counts.iteritems(): + p = 1.0 * v / len(data) + e -= p * math.log(p, 256) + return e + +class TestEntropy(common.TrezorTest): + + def test_entropy(self): + for l in [0, 1, 2, 3, 4, 5, 8, 9, 16, 17, 32, 33, 64, 65, 128, 129, 256, 257, 512, 513, 1024]: + self.client.set_expected_responses([proto.ButtonRequest(code=proto_types.ButtonRequest_Other), proto.Entropy()]) + ent = self.client.get_entropy(l) + self.assertTrue(len(ent) >= l) + print 'entropy = ', entropy(ent) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_msg_loaddevice.py b/tests/test_msg_loaddevice.py new file mode 100644 index 0000000..c7f27ff --- /dev/null +++ b/tests/test_msg_loaddevice.py @@ -0,0 +1,68 @@ +import unittest +import common + +from trezorlib import messages_pb2 as messages + +class TestDeviceLoad(common.TrezorTest): + + def test_load_device_1(self): + self.setup_mnemonic_nopin_nopassphrase() + + mnemonic = self.client.debug.read_mnemonic() + self.assertEqual(mnemonic, self.mnemonic12) + + pin = self.client.debug.read_pin()[0] + self.assertEqual(pin, '') + + passphrase_protection = self.client.debug.read_passphrase_protection() + self.assertEqual(passphrase_protection, False) + + def test_load_device_2(self): + self.setup_mnemonic_pin_passphrase() + + mnemonic = self.client.debug.read_mnemonic() + self.assertEqual(mnemonic, self.mnemonic12) + + pin = self.client.debug.read_pin()[0] + self.assertEqual(pin, self.pin4) + + passphrase_protection = self.client.debug.read_passphrase_protection() + self.assertEqual(passphrase_protection, True) + + def test_load_device_utf(self): + words_nfkd = u'Pr\u030ci\u0301s\u030cerne\u030c z\u030clut\u030couc\u030cky\u0301 ku\u030an\u030c u\u0301pe\u030cl d\u030ca\u0301belske\u0301 o\u0301dy za\u0301ker\u030cny\u0301 uc\u030cen\u030c be\u030cz\u030ci\u0301 pode\u0301l zo\u0301ny u\u0301lu\u030a' + words_nfc = u'P\u0159\xed\u0161ern\u011b \u017elu\u0165ou\u010dk\xfd k\u016f\u0148 \xfap\u011bl \u010f\xe1belsk\xe9 \xf3dy z\xe1ke\u0159n\xfd u\u010de\u0148 b\u011b\u017e\xed pod\xe9l z\xf3ny \xfal\u016f' + words_nfkc = u'P\u0159\xed\u0161ern\u011b \u017elu\u0165ou\u010dk\xfd k\u016f\u0148 \xfap\u011bl \u010f\xe1belsk\xe9 \xf3dy z\xe1ke\u0159n\xfd u\u010de\u0148 b\u011b\u017e\xed pod\xe9l z\xf3ny \xfal\u016f' + words_nfd = u'Pr\u030ci\u0301s\u030cerne\u030c z\u030clut\u030couc\u030cky\u0301 ku\u030an\u030c u\u0301pe\u030cl d\u030ca\u0301belske\u0301 o\u0301dy za\u0301ker\u030cny\u0301 uc\u030cen\u030c be\u030cz\u030ci\u0301 pode\u0301l zo\u0301ny u\u0301lu\u030a' + + passphrase_nfkd = u'Neuve\u030cr\u030citelne\u030c bezpec\u030cne\u0301 hesli\u0301c\u030cko' + passphrase_nfc = u'Neuv\u011b\u0159iteln\u011b bezpe\u010dn\xe9 hesl\xed\u010dko' + passphrase_nfkc = u'Neuv\u011b\u0159iteln\u011b bezpe\u010dn\xe9 hesl\xed\u010dko' + passphrase_nfd = u'Neuve\u030cr\u030citelne\u030c bezpec\u030cne\u0301 hesli\u0301c\u030cko' + + self.client.wipe_device() + self.client.load_device_by_mnemonic(mnemonic=words_nfkd, pin='', passphrase_protection=True, label='test', language='english', skip_checksum=True) + self.client.set_passphrase(passphrase_nfkd) + address_nfkd = self.client.get_address('Bitcoin', []) + + self.client.wipe_device() + self.client.load_device_by_mnemonic(mnemonic=words_nfc, pin='', passphrase_protection=True, label='test', language='english', skip_checksum=True) + self.client.set_passphrase(passphrase_nfc) + address_nfc = self.client.get_address('Bitcoin', []) + + self.client.wipe_device() + self.client.load_device_by_mnemonic(mnemonic=words_nfkc, pin='', passphrase_protection=True, label='test', language='english', skip_checksum=True) + self.client.set_passphrase(passphrase_nfkc) + address_nfkc = self.client.get_address('Bitcoin', []) + + self.client.wipe_device() + self.client.load_device_by_mnemonic(mnemonic=words_nfd, pin='', passphrase_protection=True, label='test', language='english', skip_checksum=True) + self.client.set_passphrase(passphrase_nfd) + address_nfd = self.client.get_address('Bitcoin', []) + + self.assertEqual(address_nfkd, address_nfc) + self.assertEqual(address_nfkd, address_nfkc) + self.assertEqual(address_nfkd, address_nfd) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_msg_recoverydevice.py b/tests/test_msg_recoverydevice.py new file mode 100644 index 0000000..1f634c7 --- /dev/null +++ b/tests/test_msg_recoverydevice.py @@ -0,0 +1,12 @@ +import unittest +import common + +from trezorlib import messages_pb2 as messages + +class TestDeviceRecover(common.TrezorTest): + + def test_recover_device(self): + pass + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_msg_resetdevice.py b/tests/test_msg_resetdevice.py new file mode 100644 index 0000000..9ddc866 --- /dev/null +++ b/tests/test_msg_resetdevice.py @@ -0,0 +1,175 @@ +import unittest +import common +import hashlib + +from trezorlib import messages_pb2 as proto +from mnemonic import Mnemonic + +def generate_entropy(strength, internal_entropy, external_entropy): + ''' + strength - length of produced seed. One of 128, 192, 256 + random - binary stream of random data from external HRNG + ''' + if strength not in (128, 192, 256): + raise Exception("Invalid strength") + + if not internal_entropy: + raise Exception("Internal entropy is not provided") + + if len(internal_entropy) < 32: + raise Exception("Internal entropy too short") + + if not external_entropy: + raise Exception("External entropy is not provided") + + if len(external_entropy) < 32: + raise Exception("External entropy too short") + + entropy = hashlib.sha256(internal_entropy + external_entropy).digest() + entropy_stripped = entropy[:strength / 8] + + if len(entropy_stripped) * 8 != strength: + raise Exception("Entropy length mismatch") + + return entropy_stripped + +class TestDeviceReset(common.TrezorTest): + + def test_reset_device(self): + # No PIN, no passphrase + external_entropy = 'zlutoucky kun upel divoke ody' * 2 + strength = 128 + + ret = self.client.call_raw(proto.ResetDevice(display_random=False, + strength=strength, + passphrase_protection=False, + pin_protection=False, + language='english', + label='test')) + + self.assertIsInstance(ret, proto.ButtonRequest) + self.client.debug.press_yes() + ret = self.client.call_raw(proto.ButtonAck()) + + self.assertIsInstance(ret, proto.EntropyRequest) + ret = self.client.call_raw(proto.EntropyAck(entropy=external_entropy)) + + # Read internal entropy and generate mnemonic locally + internal_entropy = self.client.debug.read_entropy() + entropy = generate_entropy(strength, internal_entropy, external_entropy) + expected_mnemonic = Mnemonic('english').to_mnemonic(entropy) + + mnemonic = [] + for _ in range(12): + self.assertIsInstance(ret, proto.ButtonRequest) + mnemonic.append(self.client.debug.read_word()[0]) + self.client.debug.press_yes() + self.client.call_raw(proto.ButtonAck()) + + mnemonic = ' '.join(mnemonic) + + # Compare that device generated proper mnemonic for given entropies + self.assertEqual(mnemonic, expected_mnemonic) + + mnemonic = [] + for _ in range(12): + self.assertIsInstance(ret, proto.ButtonRequest) + mnemonic.append(self.client.debug.read_word()[0]) + self.client.debug.press_yes() + resp = self.client.call_raw(proto.ButtonAck()) + + self.assertIsInstance(resp, proto.Success) + + mnemonic = ' '.join(mnemonic) + + # Compare that second pass printed out the same mnemonic once again + self.assertEqual(mnemonic, expected_mnemonic) + + # Check if device is properly initialized + resp = self.client.call_raw(proto.Initialize()) + self.assertFalse(resp.pin_protection) + self.assertFalse(resp.passphrase_protection) + + # Do passphrase-protected action, PassphraseRequest should NOT be raised + resp = self.client.call_raw(proto.Ping(passphrase_protection=True)) + self.assertIsInstance(resp, proto.Success) + + # Do PIN-protected action, PinRequest should NOT be raised + resp = self.client.call_raw(proto.Ping(pin_protection=True)) + self.assertIsInstance(resp, proto.Success) + + def test_reset_device_pin(self): + external_entropy = 'zlutoucky kun upel divoke ody' * 2 + strength = 128 + + ret = self.client.call_raw(proto.ResetDevice(display_random=True, + strength=strength, + passphrase_protection=True, + pin_protection=True, + language='english', + label='test')) + + self.assertIsInstance(ret, proto.ButtonRequest) + self.client.debug.press_yes() + ret = self.client.call_raw(proto.ButtonAck()) + + self.assertIsInstance(ret, proto.EntropyRequest) + ret = self.client.call_raw(proto.EntropyAck(entropy=external_entropy)) + + self.assertIsInstance(ret, proto.PinMatrixRequest) + + # Enter PIN for first time + pin_encoded = self.client.debug.encode_pin('654') + ret = self.client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) + self.assertIsInstance(ret, proto.PinMatrixRequest) + + # Enter PIN for second time + pin_encoded = self.client.debug.encode_pin('654') + ret = self.client.call_raw(proto.PinMatrixAck(pin=pin_encoded)) + + # Read internal entropy and generate mnemonic locally + internal_entropy = self.client.debug.read_entropy() + entropy = generate_entropy(strength, internal_entropy, external_entropy) + expected_mnemonic = Mnemonic('english').to_mnemonic(entropy) + + mnemonic = [] + for _ in range(12): + self.assertIsInstance(ret, proto.ButtonRequest) + mnemonic.append(self.client.debug.read_word()[0]) + self.client.debug.press_yes() + self.client.call_raw(proto.ButtonAck()) + + mnemonic = ' '.join(mnemonic) + + # Compare that device generated proper mnemonic for given entropies + self.assertEqual(mnemonic, expected_mnemonic) + + mnemonic = [] + for _ in range(12): + self.assertIsInstance(ret, proto.ButtonRequest) + mnemonic.append(self.client.debug.read_word()[0]) + self.client.debug.press_yes() + resp = self.client.call_raw(proto.ButtonAck()) + + self.assertIsInstance(resp, proto.Success) + + mnemonic = ' '.join(mnemonic) + + # Compare that second pass printed out the same mnemonic once again + self.assertEqual(mnemonic, expected_mnemonic) + + # Check if device is properly initialized + resp = self.client.call_raw(proto.Initialize()) + self.assertTrue(resp.pin_protection) + self.assertTrue(resp.passphrase_protection) + + # Do passphrase-protected action, PassphraseRequest should be raised + resp = self.client.call_raw(proto.Ping(passphrase_protection=True)) + self.assertIsInstance(resp, proto.PassphraseRequest) + + # Do PIN-protected action, PinRequest should be raised + resp = self.client.call_raw(proto.Ping(pin_protection=True)) + self.assertIsInstance(resp, proto.PinMatrixRequest) + +if __name__ == '__main__': + unittest.main() diff --git a/tests/test_msg_wipedevice.py b/tests/test_msg_wipedevice.py new file mode 100644 index 0000000..0f9c70a --- /dev/null +++ b/tests/test_msg_wipedevice.py @@ -0,0 +1,25 @@ +import unittest +import common + +from trezorlib import messages_pb2 as proto + +class TestDeviceWipe(common.TrezorTest): + def test_wipe_device(self): + self.setup_mnemonic_pin_passphrase() + features = self.client.call_raw(proto.Initialize()) + + self.assertEqual(features.initialized, True) + self.assertEqual(features.pin_protection, True) + self.assertEqual(features.passphrase_protection, True) + device_id = features.device_id + + self.client.wipe_device() + features = self.client.call_raw(proto.Initialize()) + + self.assertEqual(features.initialized, False) + self.assertEqual(features.pin_protection, False) + self.assertEqual(features.passphrase_protection, False) + self.assertNotEqual(features.device_id, device_id) + +if __name__ == '__main__': + unittest.main()