test_iso.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. # -*- coding: utf8 -*-
  2. # libray - Libre Blu-Ray PS3 ISO Tool
  3. # Copyright © 2018 - 2024 Nichlas Severinsen
  4. #
  5. # This file is part of libray.
  6. #
  7. # libray is free software: you can redistribute it and/or modify
  8. # it under the terms of the GNU General Public License as published by
  9. # the Free Software Foundation, either version 3 of the License, or
  10. # (at your option) any later version.
  11. #
  12. # libray is distributed in the hope that it will be useful,
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. # GNU General Public License for more details.
  16. #
  17. # You should have received a copy of the GNU General Public License
  18. # along with libray. If not, see <https://www.gnu.org/licenses/>.
  19. # This file contains tests to validate the key-retrieval logic for the
  20. # ISO class.
  21. # This script is not included in the release of libray.
  22. import argparse
  23. import unittest
  24. import unittest.mock as mock
  25. from Crypto.Cipher import AES
  26. from libray import core, iso
  27. class TestISO(unittest.TestCase):
  28. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  29. def test_explicit_decryption_key(self, mock_args):
  30. mock_args.iso = 'fake.iso'
  31. mock_args.decryption_key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  32. decryptkey_bytes = core.to_bytes(mock_args.decryption_key)
  33. fake_iso = iso.ISO.__new__(iso.ISO)
  34. returned_key = fake_iso.get_key_from_args('AAA', mock_args)
  35. self.assertEqual(decryptkey_bytes, returned_key)
  36. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  37. def test_explicit_valid_ird(self, mock_args):
  38. mock_args.iso = 'fake.iso'
  39. mock_args.ird = 'aaa.ird'
  40. mock_args.decryption_key = False
  41. mock_args.verbose = True
  42. ird = mock.Mock()
  43. ird.region_count = 3
  44. ird.data1 = b'01010101010101010101010101010101'
  45. cipher = AES.new(core.ISO_SECRET, AES.MODE_CBC, core.ISO_IV)
  46. decryptkey_bytes = cipher.encrypt(ird.data1)
  47. fake_iso = iso.ISO.__new__(iso.ISO)
  48. fake_iso.size = 2048
  49. fake_iso.regions = [
  50. {'start': 0, 'end': 512, 'enc': False},
  51. {'start': 512, 'end': 1024, 'enc': True},
  52. {'start': 1024, 'end': 2048, 'enc': False}
  53. ]
  54. with mock.patch('iso.ird.IRD', return_value=ird) as mock_ird:
  55. returned_key = fake_iso.get_key_from_args('AAA', mock_args)
  56. mock_ird.assert_called_once_with('aaa.ird')
  57. self.assertEqual(decryptkey_bytes, returned_key)
  58. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  59. def test_ird_with_region_count_mismatch(self, mock_args):
  60. mock_args.iso = 'fake.iso'
  61. mock_args.ird = 'aaa.ird'
  62. mock_args.decryption_key = False
  63. mock_args.verbose = True
  64. ird = mock.Mock()
  65. ird.region_count = 3
  66. ird.data1 = b'01010101010101010101010101010101'
  67. fake_iso = iso.ISO.__new__(iso.ISO)
  68. fake_iso.size = 512
  69. fake_iso.regions = [
  70. {'start': 0, 'end': 512, 'enc': False},
  71. ]
  72. with mock.patch('iso.ird.IRD', return_value=ird) as mock_ird:
  73. with self.assertRaises(SystemExit):
  74. fake_iso.get_key_from_args('AAA', mock_args)
  75. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  76. def test_ird_with_invalid_start(self, mock_args):
  77. mock_args.iso = 'fake.iso'
  78. mock_args.ird = 'aaa.ird'
  79. mock_args.decryption_key = False
  80. mock_args.verbose = True
  81. ird = mock.Mock()
  82. ird.region_count = 3
  83. ird.data1 = b'01010101010101010101010101010101'
  84. fake_iso = iso.ISO.__new__(iso.ISO)
  85. fake_iso.size = 512 * 1024 * 1024
  86. fake_iso.regions = [
  87. {'start': 0, 'end': 3000000, 'enc': False},
  88. {'start': 3000000, 'end': 2000000000, 'enc': True},
  89. {'start': 2000000000, 'end': 2000001000, 'enc': False}
  90. ]
  91. with mock.patch('iso.ird.IRD', return_value=ird) as mock_ird:
  92. with self.assertRaises(SystemExit):
  93. fake_iso.get_key_from_args('AAA', mock_args)
  94. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  95. def test_keys_db_size_match(self, mock_args):
  96. mock_args.iso = 'fake.iso'
  97. mock_args.ird = ''
  98. mock_args.decryption_key = False
  99. mock_args.verbose = True
  100. fake_iso = iso.ISO.__new__(iso.ISO)
  101. fake_iso.size = 512 * 1024 * 1024
  102. fake_iso.game_id = 'TCUS-12345'
  103. with mock.patch('iso.sqlite3') as mocksql:
  104. decryption_key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  105. decryptkey_bytes = core.to_bytes(decryption_key)
  106. mocksql.connect().cursor().execute().fetchall.return_value = [['AAA', decryptkey_bytes]]
  107. returned_key = fake_iso.get_key_from_args('AAA', mock_args)
  108. self.assertEqual(decryptkey_bytes, returned_key)
  109. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  110. def test_keys_db_size_multiple_match_name_lookup(self, mock_args):
  111. mock_args.iso = 'fake.iso'
  112. mock_args.ird = ''
  113. mock_args.decryption_key = False
  114. mock_args.verbose = True
  115. fake_iso = iso.ISO.__new__(iso.ISO)
  116. fake_iso.size = 512 * 1024 * 1024
  117. fake_iso.game_id = 'TCUS-12345'
  118. with mock.patch('iso.sqlite3') as mocksql:
  119. decryption_key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  120. decryptkey_bytes = core.to_bytes(decryption_key)
  121. mocksql.connect().cursor().execute().fetchall.side_effect = [[], [['AAA', decryptkey_bytes]]]
  122. returned_key = fake_iso.get_key_from_args('AAA', mock_args)
  123. self.assertEqual(decryptkey_bytes, returned_key)
  124. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  125. def test_keys_db_size_multiple_match_no_game_id(self, mock_args):
  126. mock_args.iso = 'fake.iso'
  127. mock_args.ird = ''
  128. mock_args.decryption_key = False
  129. mock_args.verbose = True
  130. fake_iso = iso.ISO.__new__(iso.ISO)
  131. fake_iso.size = 512 * 1024 * 1024
  132. fake_iso.game_id = 'TCUS-12345'
  133. with mock.patch('iso.sqlite3') as mocksql:
  134. decryption_key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  135. decryptkey_bytes = core.to_bytes(decryption_key)
  136. mocksql.connect().cursor().execute().fetchall.return_value = [['AAA', decryptkey_bytes],['BBB', decryptkey_bytes]]
  137. with self.assertRaises(ValueError):
  138. fake_iso.get_key_from_args(None, mock_args)
  139. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  140. def test_keys_db_no_match_no_checksum(self, mock_args):
  141. mock_args.iso = 'fake.iso'
  142. mock_args.ird = ''
  143. mock_args.decryption_key = False
  144. mock_args.checksum = False
  145. mock_args.verbose = True
  146. fake_iso = iso.ISO.__new__(iso.ISO)
  147. fake_iso.size = 512 * 1024 * 1024
  148. fake_iso.game_id = 'TCUS-12345'
  149. with mock.patch('iso.sqlite3') as mocksql:
  150. mocksql.connect().cursor().execute().fetchall.return_value = []
  151. with self.assertRaises(SystemExit):
  152. fake_iso.get_key_from_args('AAA', mock_args)
  153. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  154. def test_keys_db_no_match_checksum_fallback(self, mock_args):
  155. mock_args.iso = 'fake.iso'
  156. mock_args.ird = ''
  157. mock_args.decryption_key = False
  158. mock_args.checksum = True
  159. mock_args.checksum_timeout = 15
  160. mock_args.verbose = True
  161. fake_iso = iso.ISO.__new__(iso.ISO)
  162. fake_iso.size = 512 * 1024 * 1024
  163. fake_iso.game_id = 'TCUS-12345'
  164. with mock.patch('iso.sqlite3') as mocksql:
  165. decryption_key = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'
  166. decryptkey_bytes = core.to_bytes(decryption_key)
  167. fakeresults = ([], [], [['AAA', decryptkey_bytes]])
  168. mocksql.connect().cursor().execute().fetchall.side_effect = fakeresults
  169. with mock.patch('iso.core.crc32', return_value='01010101'):
  170. returned_key = fake_iso.get_key_from_args('AAA', mock_args)
  171. self.assertEqual(decryptkey_bytes, returned_key)
  172. @mock.patch('argparse.ArgumentParser.parse_args', return_value=argparse.Namespace())
  173. def test_keys_db_no_match_checksum_timeout(self, mock_args):
  174. mock_args.iso = 'fake.iso'
  175. mock_args.ird = ''
  176. mock_args.decryption_key = False
  177. mock_args.checksum = True
  178. mock_args.checksum_timeout = 15
  179. mock_args.verbose = True
  180. fake_iso = iso.ISO.__new__(iso.ISO)
  181. fake_iso.size = 512 * 1024 * 1024
  182. fake_iso.game_id = 'TCUS-12345'
  183. with mock.patch('iso.sqlite3') as mocksql:
  184. mocksql.connect().cursor().execute().fetchall.return_value = []
  185. with mock.patch('iso.core.crc32', return_value=None):
  186. with self.assertRaises(TimeoutError):
  187. fake_iso.get_key_from_args('AAA', mock_args)