mirror of
https://gitlab.com/fdroid/fdroidserver.git
synced 2024-08-18 04:10:10 +02:00
3182b77d18
The current signing method uses apksigner to sign the JAR so that it will automatically select algorithms that are compatible with Android SDK 23, which added the most recent algorithms: https://developer.android.com/reference/java/security/Signature This signing method uses then inherits the default signing algothim settings, since Java and Android both maintain those. That helps avoid a repeat of being stuck on an old signing algorithm. That means specifically that this call to apksigner does not specify any of the algorithms. The old indexes must be signed by SHA1withRSA otherwise they will no longer be compatible with old Androids. apksigner 30.0.0+ is available in Debian/bullseye, Debian/buster-backports, Ubuntu 21.10, and Ubuntu 20.04 from the fdroid PPA. Here's a quick way to test: for f in `ls -1 /opt/android-sdk/build-tools/*/apksigner | sort ` /usr/bin/apksigner; do printf "$f : "; $f sign --v4-signing-enabled false; done closes #1005
146 lines
4.7 KiB
Python
Executable File
146 lines
4.7 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
import inspect
|
|
import json
|
|
import logging
|
|
import optparse
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
localmodule = os.path.realpath(
|
|
os.path.join(os.path.dirname(inspect.getfile(inspect.currentframe())), '..')
|
|
)
|
|
print('localmodule: ' + localmodule)
|
|
if localmodule not in sys.path:
|
|
sys.path.insert(0, localmodule)
|
|
|
|
from fdroidserver import apksigcopier, common, signindex, update
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
|
|
class Options:
|
|
allow_disabled_algorithms = False
|
|
clean = False
|
|
delete_unknown = False
|
|
nosign = False
|
|
pretty = True
|
|
rename_apks = False
|
|
verbose = False
|
|
|
|
|
|
class SignindexTest(unittest.TestCase):
|
|
|
|
basedir = Path(__file__).resolve().parent
|
|
|
|
def setUp(self):
|
|
signindex.config = None
|
|
config = common.read_config(common.options)
|
|
config['jarsigner'] = common.find_sdk_tools_cmd('jarsigner')
|
|
config['verbose'] = True
|
|
config['keystore'] = str(self.basedir / 'keystore.jks')
|
|
config['repo_keyalias'] = 'sova'
|
|
config['keystorepass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
|
config['keypass'] = 'r9aquRHYoI8+dYz6jKrLntQ5/NJNASFBacJh7Jv2BlI='
|
|
signindex.config = config
|
|
|
|
logging.basicConfig(level=logging.DEBUG)
|
|
self.tempdir = tempfile.TemporaryDirectory()
|
|
os.chdir(self.tempdir.name)
|
|
self.repodir = Path('repo')
|
|
self.repodir.mkdir()
|
|
|
|
def tearDown(self):
|
|
self.tempdir.cleanup()
|
|
|
|
def test_sign_index(self):
|
|
shutil.copy(str(self.basedir / 'repo/index-v1.json'), 'repo')
|
|
signindex.sign_index(str(self.repodir), 'index-v1.json')
|
|
self.assertTrue((self.repodir / 'index-v1.jar').exists())
|
|
self.assertTrue((self.repodir / 'index-v1.json').exists())
|
|
|
|
def test_sign_index_corrupt(self):
|
|
with open('repo/index-v1.json', 'w') as fp:
|
|
fp.write('corrupt JSON!')
|
|
with self.assertRaises(json.decoder.JSONDecodeError, msg='error on bad JSON'):
|
|
signindex.sign_index(str(self.repodir), 'index-v1.json')
|
|
|
|
def test_signindex(self):
|
|
os.mkdir('archive')
|
|
metadata = Path('metadata')
|
|
metadata.mkdir()
|
|
with (metadata / 'info.guardianproject.urzip.yml').open('w') as fp:
|
|
fp.write('# placeholder')
|
|
shutil.copy(str(self.basedir / 'urzip.apk'), 'repo')
|
|
index_files = []
|
|
for f in (
|
|
'entry.jar',
|
|
'entry.json',
|
|
'index-v1.jar',
|
|
'index-v1.json',
|
|
'index-v2.json',
|
|
'index.jar',
|
|
'index.xml',
|
|
):
|
|
for section in (Path('repo'), Path('archive')):
|
|
path = section / f
|
|
self.assertFalse(path.exists(), '%s should not exist yet!' % path)
|
|
index_files.append(path)
|
|
common.options = Options
|
|
with patch('sys.argv', ['fdroid update']):
|
|
update.main()
|
|
with patch('sys.argv', ['fdroid signindex', '--verbose']):
|
|
signindex.main()
|
|
for f in index_files:
|
|
self.assertTrue(f.exists(), '%s should exist!' % f)
|
|
self.assertFalse(os.path.exists('index-v2.jar')) # no JAR version of this file
|
|
|
|
# index.jar aka v0 must by signed by SHA1withRSA
|
|
f = 'repo/index.jar'
|
|
common.verify_jar_signature(f)
|
|
self.assertIsNone(apksigcopier.extract_v2_sig(f, expected=False))
|
|
cp = subprocess.run(
|
|
['jarsigner', '-verify', '-verbose', f], stdout=subprocess.PIPE
|
|
)
|
|
self.assertTrue(b'SHA1withRSA' in cp.stdout)
|
|
|
|
# index-v1.jar must by signed by SHA1withRSA
|
|
f = 'repo/index-v1.jar'
|
|
common.verify_jar_signature(f)
|
|
self.assertIsNone(apksigcopier.extract_v2_sig(f, expected=False))
|
|
cp = subprocess.run(
|
|
['jarsigner', '-verify', '-verbose', f], stdout=subprocess.PIPE
|
|
)
|
|
self.assertTrue(b'SHA1withRSA' in cp.stdout)
|
|
|
|
# entry.jar aka index v2 must by signed by a modern algorithm
|
|
f = 'repo/entry.jar'
|
|
common.verify_jar_signature(f)
|
|
self.assertIsNone(apksigcopier.extract_v2_sig(f, expected=False))
|
|
cp = subprocess.run(
|
|
['jarsigner', '-verify', '-verbose', f], stdout=subprocess.PIPE
|
|
)
|
|
self.assertFalse(b'SHA1withRSA' in cp.stdout)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
os.chdir(os.path.dirname(__file__))
|
|
|
|
parser = optparse.OptionParser()
|
|
parser.add_option(
|
|
"-v",
|
|
"--verbose",
|
|
action="store_true",
|
|
default=False,
|
|
help="Spew out even more information than normal",
|
|
)
|
|
(common.options, args) = parser.parse_args(['--verbose'])
|
|
|
|
newSuite = unittest.TestSuite()
|
|
newSuite.addTest(unittest.makeSuite(SignindexTest))
|
|
unittest.main(failfast=False)
|