From 86865faa629ace1f0077e7ca5e19ad65634c118f Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Mon, 20 Apr 2015 19:09:50 -0400 Subject: [PATCH] make `fdroid update` check that it can sign the repo, or exit with error There is no good reason to run unsigned repos any more. It is trivially easy to create and use a signed repo, and having to support unsigned repos in the client makes some security-critical parts of the code a lot more complicated. refs #13 https://gitlab.com/fdroid/fdroidserver/issues/13 https://gitlab.com/fdroid/fdroidclient/issues/12 --- fdroidserver/update.py | 82 ++++++++++++++++++++++++++---------------- tests/run-tests | 68 +++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 31 deletions(-) diff --git a/fdroidserver/update.py b/fdroidserver/update.py index f5eee07c..2aa18618 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -664,6 +664,36 @@ def scan_apks(apps, apkcache, repodir, knownapks): repo_pubkey_fingerprint = None +# Generate a certificate fingerprint the same way keytool does it +# (but with slightly different formatting) +def cert_fingerprint(data): + digest = hashlib.sha256(data).digest() + ret = [] + ret.append(' '.join("%02X" % ord(b) for b in digest)) + return " ".join(ret) + + +def extract_pubkey(): + global repo_pubkey_fingerprint + if 'repo_pubkey' in config: + pubkey = unhexlify(config['repo_pubkey']) + else: + p = FDroidPopen(['keytool', '-exportcert', + '-alias', config['repo_keyalias'], + '-keystore', config['keystore'], + '-storepass:file', config['keystorepassfile']] + + config['smartcardoptions'], output=False) + if p.returncode != 0 or len(p.output) < 20: + msg = "Failed to get repo pubkey!" + if config['keystore'] == 'NONE': + msg += ' Is your crypto smartcard plugged in?' + logging.critical(msg) + sys.exit(1) + pubkey = p.output + repo_pubkey_fingerprint = cert_fingerprint(pubkey) + return hexlify(pubkey) + + def make_index(apps, sortedids, apks, repodir, archive, categories): """Make a repo index. @@ -711,38 +741,28 @@ def make_index(apps, sortedids, apks, repodir, archive, categories): repoel.setAttribute("version", "12") repoel.setAttribute("timestamp", str(int(time.time()))) - if 'repo_keyalias' in config: - - # Generate a certificate fingerprint the same way keytool does it - # (but with slightly different formatting) - def cert_fingerprint(data): - digest = hashlib.sha256(data).digest() - ret = [] - ret.append(' '.join("%02X" % ord(b) for b in digest)) - return " ".join(ret) - - def extract_pubkey(): - global repo_pubkey_fingerprint - if 'repo_pubkey' in config: - pubkey = unhexlify(config['repo_pubkey']) - else: - p = FDroidPopen(['keytool', '-exportcert', - '-alias', config['repo_keyalias'], - '-keystore', config['keystore'], - '-storepass:file', config['keystorepassfile']] - + config['smartcardoptions'], output=False) - if p.returncode != 0: - msg = "Failed to get repo pubkey!" - if config['keystore'] == 'NONE': - msg += ' Is your crypto smartcard plugged in?' - logging.critical(msg) - sys.exit(1) - pubkey = p.output - repo_pubkey_fingerprint = cert_fingerprint(pubkey) - return hexlify(pubkey) - - repoel.setAttribute("pubkey", extract_pubkey()) + nosigningkey = False + if not 'repo_keyalias' in config: + nosigningkey = True + logging.critical("'repo_keyalias' not found in config.py!") + if not 'keystore' in config: + nosigningkey = True + logging.critical("'keystore' not found in config.py!") + if not 'keystorepass' in config: + nosigningkey = True + logging.critical("'keystorepass' not found in config.py!") + if not 'keypass' in config: + nosigningkey = True + logging.critical("'keypass' not found in config.py!") + if not os.path.exists(config['keystore']): + nosigningkey = True + logging.critical("'" + config['keystore'] + "' does not exist!") + if nosigningkey: + logging.warning("`fdroid update` requires a signing key, you can create one using:") + logging.warning("\tfdroid update --create-key") + sys.exit(1) + repoel.setAttribute("pubkey", extract_pubkey()) root.appendChild(repoel) for appid in sortedids: diff --git a/tests/run-tests b/tests/run-tests index d1f988fa..8ee3b6f8 100755 --- a/tests/run-tests +++ b/tests/run-tests @@ -325,6 +325,74 @@ $fdroid init --keystore NONE test -e opensc-fdroid.cfg test ! -e NONE + +#------------------------------------------------------------------------------# +echo_header "setup a new repo with no keystore, add APK, and update" + +REPOROOT=`create_test_dir` +KEYSTORE=$REPOROOT/keystore.jks +cd $REPOROOT +touch config.py +touch fdroid-icon.png +mkdir repo/ +cp $WORKSPACE/tests/urzip.apk $REPOROOT/ +set +e +$fdroid update --create-metadata +if [ $? -eq 0 ]; then + echo "This should have failed because this repo has no keystore!" + exit 1 +else + echo "`fdroid update` prompted to add keystore" +fi +set -e + +# now set up fake, non-working keystore setup +touch $KEYSTORE +echo "keystore = \"$KEYSTORE\"" >> config.py +echo 'repo_keyalias = "foo"' >> config.py +echo 'keystorepass = "foo"' >> config.py +echo 'keypass = "foo"' >> config.py +set +e +$fdroid update --create-metadata +if [ $? -eq 0 ]; then + echo "This should have failed because this repo has a bad/fake keystore!" + exit 1 +else + echo "`fdroid update` prompted to add keystore" +fi +set -e + + +#------------------------------------------------------------------------------# +echo_header "setup a new repo with keystore with APK, update, then without key" + +REPOROOT=`create_test_dir` +KEYSTORE=$REPOROOT/keystore.jks +cd $REPOROOT +$fdroid init --keystore $KEYSTORE +test -e $KEYSTORE +cp $WORKSPACE/tests/urzip.apk $REPOROOT/repo/ +$fdroid update --create-metadata +test -e repo/index.xml +test -e repo/index.jar +grep -F '