9 from unittest.mock import patch
15 localmodule = os.path.realpath(
16 os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..'))
17 print('localmodule: ' + localmodule)
18 if localmodule not in sys.path:
19 sys.path.insert(0, localmodule)
21 import fdroidserver.common
22 import fdroidserver.index
23 import fdroidserver.signindex
24 import fdroidserver.publish
25 from testcommon import TmpCwd
28 GP_FINGERPRINT = 'B7C2EEFD8DAC7806AF67DFCD92EB18126BC08312A7F2D6F3862E46013C7A6135'
31 class IndexTest(unittest.TestCase):
34 fdroidserver.common.config = None
35 config = fdroidserver.common.read_config(fdroidserver.common.options)
36 config['jarsigner'] = fdroidserver.common.find_sdk_tools_cmd('jarsigner')
37 fdroidserver.common.config = config
38 fdroidserver.signindex.config = config
41 def test_verify_jar_signature_succeeds():
42 basedir = os.path.dirname(__file__)
43 source_dir = os.path.join(basedir, 'signindex')
44 for f in ('testy.jar', 'guardianproject.jar'):
45 testfile = os.path.join(source_dir, f)
46 fdroidserver.common.verify_jar_signature(testfile)
48 def test_verify_jar_signature_fails(self):
49 basedir = os.path.dirname(__file__)
50 source_dir = os.path.join(basedir, 'signindex')
51 testfile = os.path.join(source_dir, 'unsigned.jar')
52 with self.assertRaises(fdroidserver.index.VerificationException):
53 fdroidserver.common.verify_jar_signature(testfile)
55 def test_get_public_key_from_jar_succeeds(self):
56 basedir = os.path.dirname(__file__)
57 source_dir = os.path.join(basedir, 'signindex')
58 for f in ('testy.jar', 'guardianproject.jar'):
59 testfile = os.path.join(source_dir, f)
60 jar = zipfile.ZipFile(testfile)
61 _, fingerprint = fdroidserver.index.get_public_key_from_jar(jar)
62 # comparing fingerprints should be sufficient
64 self.assertTrue(fingerprint ==
65 '818E469465F96B704E27BE2FEE4C63AB' +
66 '9F83DDF30E7A34C7371A4728D83B0BC1')
67 if f == 'guardianproject.jar':
68 self.assertTrue(fingerprint == GP_FINGERPRINT)
70 def test_get_public_key_from_jar_fails(self):
71 basedir = os.path.dirname(__file__)
72 source_dir = os.path.join(basedir, 'signindex')
73 testfile = os.path.join(source_dir, 'unsigned.jar')
74 jar = zipfile.ZipFile(testfile)
75 with self.assertRaises(fdroidserver.index.VerificationException):
76 fdroidserver.index.get_public_key_from_jar(jar)
78 def test_download_repo_index_no_fingerprint(self):
79 with self.assertRaises(fdroidserver.index.VerificationException):
80 fdroidserver.index.download_repo_index("http://example.org")
82 def test_download_repo_index_no_jar(self):
83 with self.assertRaises(requests.exceptions.HTTPError):
84 fdroidserver.index.download_repo_index("http://example.org?fingerprint=nope")
86 @patch('requests.head')
87 def test_download_repo_index_same_etag(self, head):
88 url = 'http://example.org?fingerprint=test'
89 etag = '"4de5-54d840ce95cb9"'
91 head.return_value.headers = {'ETag': etag}
92 index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
94 self.assertIsNone(index)
95 self.assertEqual(etag, new_etag)
97 @patch('requests.get')
98 @patch('requests.head')
99 def test_download_repo_index_new_etag(self, head, get):
100 url = 'http://example.org?fingerprint=' + GP_FINGERPRINT
101 etag = '"4de5-54d840ce95cb9"'
104 head.return_value.headers = {'ETag': 'new_etag'}
105 get.return_value.headers = {'ETag': 'new_etag'}
106 get.return_value.status_code = 200
107 testfile = os.path.join(os.path.dirname(__file__), 'signindex', 'guardianproject-v1.jar')
108 with open(testfile, 'rb') as file:
109 get.return_value.content = file.read()
111 index, new_etag = fdroidserver.index.download_repo_index(url, etag=etag)
113 # assert that the index was retrieved properly
114 self.assertEqual('Guardian Project Official Releases', index['repo']['name'])
115 self.assertEqual(GP_FINGERPRINT, index['repo']['fingerprint'])
116 self.assertTrue(len(index['repo']['pubkey']) > 500)
117 self.assertEqual(10, len(index['apps']))
118 self.assertEqual(10, len(index['packages']))
119 self.assertEqual('new_etag', new_etag)
121 def test_v1_sort_packages(self):
123 i = [{'packageName': 'org.smssecure.smssecure',
124 'apkName': 'org.smssecure.smssecure_134.apk',
125 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
127 {'packageName': 'org.smssecure.smssecure',
128 'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
129 'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
131 {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
132 {'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
133 {'packageName': 'org.smssecure.smssecure',
134 'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
135 'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
137 {'packageName': 'org.smssecure.smssecure',
138 'apkName': 'org.smssecure.smssecure_135.apk',
139 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
141 {'packageName': 'org.smssecure.smssecure',
142 'apkName': 'org.smssecure.smssecure_133.apk',
143 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
145 {'packageName': 'org.smssecure.smssecure',
146 'apkName': 'smssecure-weird-version.apk',
147 'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
149 {'packageName': 'org.smssecure.smssecure',
150 'apkName': 'smssecure-custom.apk',
151 'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
153 {'packageName': 'org.smssecure.smssecure',
154 'apkName': 'smssecure-new-custom.apk',
155 'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
158 o = [{'packageName': '43af70d1aca437c2f9974c4634cc5abe45bdc4d5d71529ac4e553488d3bb3ff6'},
159 {'packageName': 'b075b32b4ef1e8a869e00edb136bd48e34a0382b85ced8628f164d1199584e4e'},
161 # # packages with reproducible developer signature
162 {'packageName': 'org.smssecure.smssecure',
163 'apkName': 'org.smssecure.smssecure_135_b30bb97.apk',
164 'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
166 {'packageName': 'org.smssecure.smssecure',
167 'apkName': 'org.smssecure.smssecure_134_b30bb97.apk',
168 'signer': 'b30bb971af0d134866e158ec748fcd553df97c150f58b0a963190bbafbeb0868',
170 # # packages build and signed by fdroid
171 {'packageName': 'org.smssecure.smssecure',
172 'apkName': 'org.smssecure.smssecure_135.apk',
173 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
175 {'packageName': 'org.smssecure.smssecure',
176 'apkName': 'org.smssecure.smssecure_134.apk',
177 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
179 {'packageName': 'org.smssecure.smssecure',
180 'apkName': 'org.smssecure.smssecure_133.apk',
181 'signer': 'b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6',
183 # # packages signed with unkown keys
184 {'packageName': 'org.smssecure.smssecure',
185 'apkName': 'smssecure-new-custom.apk',
186 'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
188 {'packageName': 'org.smssecure.smssecure',
189 'apkName': 'smssecure-custom.apk',
190 'signer': '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
192 {'packageName': 'org.smssecure.smssecure',
193 'apkName': 'smssecure-weird-version.apk',
194 'signer': '99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff99ff',
197 fdroidserver.common.config = {}
198 fdroidserver.common.fill_config_defaults(fdroidserver.common.config)
199 fdroidserver.publish.config = fdroidserver.common.config
200 fdroidserver.publish.config['keystorepass'] = '123456'
201 fdroidserver.publish.config['keypass'] = '123456'
202 fdroidserver.publish.config['keystore'] = os.path.join(os.getcwd(),
203 'dummy-keystore.jks')
204 fdroidserver.publish.config['repo_keyalias'] = 'repokey'
206 testsmetadir = os.path.join(os.getcwd(), 'metadata')
207 with tempfile.TemporaryDirectory() as tmpdir, TmpCwd(tmpdir):
208 shutil.copytree(testsmetadir, 'metadata')
210 "org.smssecure.smssecure": {
211 "signer": "b33a601a9da97c82e6eb121eb6b90adab561f396602ec4dc8b0019fb587e2af6"
215 jarfile = 'stats/publishsigkeys.jar'
216 with zipfile.ZipFile(jarfile, 'w', zipfile.ZIP_DEFLATED) as jar:
217 jar.writestr('publishsigkeys.json', json.dumps(sigkeyfps))
218 fdroidserver.publish.sign_sig_key_fingerprint_list(jarfile)
219 with open('config.py', 'w'):
222 fdroidserver.index.v1_sort_packages(
223 i, 'repo', fdroidserver.common.load_stats_fdroid_signing_key_fingerprints())
225 self.assertEqual(json.dumps(i, indent=2), json.dumps(o, indent=2))
228 if __name__ == "__main__":
229 if os.path.basename(os.getcwd()) != 'tests' and os.path.isdir('tests'):
232 parser = optparse.OptionParser()
233 parser.add_option("-v", "--verbose", action="store_true", default=False,
234 help="Spew out even more information than normal")
235 (fdroidserver.common.options, args) = parser.parse_args(['--verbose'])
237 newSuite = unittest.TestSuite()
238 newSuite.addTest(unittest.makeSuite(IndexTest))