diff --git a/.gitignore b/.gitignore index 261f57bf93bcce234e96aac5322f935c7e47cbb2..0e09cdbccde7987d66a0bf8c3d6ba63aed2b3865 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ .idea *.apk +*.jar +briar/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a76cae4fe8f5616a5a759744fc58cc9418574e76..e316b7859855e09aef50c2e77fda351d63e87157 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -30,7 +30,7 @@ test_success: script: # Consider adding the cap and the device directly to the CI config # https://docs.gitlab.com/runner/configuration/advanced-configuration.html#the-runners-docker-section - - docker run --cap-add SYS_ADMIN --device /dev/fuse ${TEST_IMAGE} ./reproduce.py release-1.1.7 + - docker run --cap-add SYS_ADMIN --device /dev/fuse ${TEST_IMAGE} ./reproduce.py release-1.2.10 except: - triggers diff --git a/Dockerfile b/Dockerfile index eafb03483cfe590c2d8eb9373beb5fc1a6ae82d4..c36c0ce75562e4ae8e0c78c8c6a4da6b09fe7e18 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,8 +9,7 @@ WORKDIR /opt/briar-reproducer # add required files to WORKDIR ADD install*.sh ./ -ADD verify-apk.py ./ -ADD reproduce.py ./ +ADD *.py ./ RUN ./install.sh diff --git a/reproduce.py b/reproduce.py index 51f8f25fd617ced2a9fcefceb50d9739b3f5acfd..fa8cabfd4d1a1894dae78e7703de9f68b2a5d29d 100755 --- a/reproduce.py +++ b/reproduce.py @@ -1,15 +1,22 @@ #!/usr/bin/env python3 +import atexit import os import sys from subprocess import call, check_call, check_output +from verify_apk import verify_apk +from verify_jar import verify_jar +from utils import fail REPO_DIR = "briar" -REFERENCE_URL = 'https://briarproject.org/apk/briar-%s.apk' -GRADLE_TASK = "briar-android:assembleRelease" +REFERENCE_APK_URL = 'https://briarproject.org/apk/briar-%s.apk' +REFERENCE_JAR_URL = 'https://briarproject.org/jar/briar-headless-%s.jar' +GRADLE_APK_TASK = "briar-android:assembleRelease" +GRADLE_JAR_TASK = "briar-headless:assemble" APK_PATH = "briar-android/build/outputs/apk/official/release/" + \ "briar-android-official-release-unsigned.apk" OLD_APK_PATH = "briar-android/build/outputs/apk/release/briar-android-release-unsigned.apk" +JAR_PATH= "briar-headless/build/libs/briar-headless.jar" BUILD_DIR = "briar-build" @@ -21,28 +28,39 @@ def main(): # clone/reset repo and checkout tag tag = prepare_repo(tag) - # download reference binary (before building to detect missing file early on) + # download reference binaries (before building to detect missing file early on) version = tag.split('-')[1] - url = REFERENCE_URL % version + apk_url = REFERENCE_APK_URL % version reference_apk = "briar-%s.apk" % version - check_call(['wget', '--no-verbose', url, '-O', reference_apk]) + check_call(['wget', '--no-verbose', apk_url, '-O', reference_apk]) + + jar_url = REFERENCE_JAR_URL % version + reference_jar = "briar-headless-%s.jar" % version + if version >= "1.2.10": + check_call(['wget', '--no-verbose', jar_url, '-O', reference_jar]) # use non-deterministic file system for building the app to detect issues if not os.path.exists(BUILD_DIR): os.makedirs(BUILD_DIR) check_call(['disorderfs', '--shuffle-dirents=yes', REPO_DIR, BUILD_DIR]) + atexit.register(unmount) # build the app - check_call(["./gradlew", "--no-daemon", GRADLE_TASK], cwd=BUILD_DIR) + check_call(["./gradlew", "--no-daemon", GRADLE_APK_TASK, GRADLE_JAR_TASK], cwd=BUILD_DIR) # check if both APKs match apk = os.path.join(BUILD_DIR, APK_PATH if version >= "1.1.0" else OLD_APK_PATH) - if call(['./verify-apk.py', reference_apk, apk]) == 0: - print("Version '%s' was built reproducible! :)" % tag) - sys.exit(0) - else: + if not verify_apk(reference_apk, apk): sys.exit(1) + # check if both jars match + if version >= "1.2.10": + jar = os.path.join(BUILD_DIR, JAR_PATH) + if not verify_jar(reference_jar, jar): + sys.exit(1) + + print("Version '%s' was built reproducible! :)" % tag) + def prepare_repo(tag): if os.path.isdir(REPO_DIR): @@ -76,9 +94,8 @@ def repo_call(command): check_call(command, cwd=REPO_DIR) -def fail(msg=""): - sys.stderr.write(msg + "\n") - sys.exit(1) +def unmount(): + call(['fusermount', '-u', BUILD_DIR]) if __name__ == "__main__": diff --git a/utils.py b/utils.py new file mode 100755 index 0000000000000000000000000000000000000000..1aa609d53cf4e3df6d3912c573f435fd7beb6e91 --- /dev/null +++ b/utils.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +import hashlib +import sys + + +def calc_hash(filename, block_size=65536): + sha512 = hashlib.sha512() + with open(filename, 'rb') as f: + for block in iter(lambda: f.read(block_size), b''): + sha512.update(block) + return sha512.hexdigest() + + +def fail(msg=""): + sys.stderr.write(msg + "\n") + sys.exit(1) + diff --git a/verify-apk.py b/verify_apk.py similarity index 73% rename from verify-apk.py rename to verify_apk.py index e7c36eade2dd65f7cbff55362a6772e52d0c419f..f6589d69bca239b43da53d1464d65dec2cba56a6 100755 --- a/verify-apk.py +++ b/verify_apk.py @@ -1,12 +1,12 @@ #!/usr/bin/env python3 -import hashlib import os import re import subprocess import sys import tempfile import zipfile +from utils import calc_hash, fail APK_SIG_FILE = re.compile(r'META-INF/[0-9A-Za-z_\-]+\.(SF|RSA|DSA|EC|MF)') @@ -14,15 +14,17 @@ APK_SIG_FILE = re.compile(r'META-INF/[0-9A-Za-z_\-]+\.(SF|RSA|DSA|EC|MF)') def main(): if len(sys.argv) != 3: fail("Usage: %s referenceAPK builtAPK" % sys.argv[0]) - verify(sys.argv[1], sys.argv[2]) + if not verify_apk(sys.argv[1], sys.argv[2]): sys.exit(1) -def verify(reference_apk, built_apk): +def verify_apk(reference_apk, built_apk): if not os.path.isfile(reference_apk): - fail("Can not verify: Reference APK '%s' does not exists" % reference_apk) + sys.stderr.write("Cannot verify: Reference APK '%s' does not exist" % reference_apk) + return False if not os.path.isfile(built_apk): - fail("Can not verify: Built APK '%s' does not exists" % built_apk) + sys.stderr.write("Cannot verify: Built APK '%s' does not exist" % built_apk) + return False with tempfile.TemporaryDirectory() as tmp_dir: # repack reference APK @@ -37,10 +39,11 @@ def verify(reference_apk, built_apk): if calc_hash(reference_tmp) != calc_hash(built_tmp): sys.stderr.write("Mismatch detected. Trying to find reason...\n") subprocess.call(['diffoscope', reference_tmp, built_tmp]) - fail("Files do NOT match! :(") + sys.stderr.write("Files do NOT match! :(") + return False else: print("Files match! :)") - sys.exit(0) + return True def repack_apk(zip_file, new_zip_file): @@ -60,18 +63,5 @@ def repack_apk(zip_file, new_zip_file): tmp.writestr(info, f.read(info.filename)) -def calc_hash(filename, block_size=65536): - sha512 = hashlib.sha512() - with open(filename, 'rb') as f: - for block in iter(lambda: f.read(block_size), b''): - sha512.update(block) - return sha512.hexdigest() - - -def fail(msg=""): - sys.stderr.write(msg + "\n") - sys.exit(1) - - if __name__ == "__main__": - main() + main() diff --git a/verify_jar.py b/verify_jar.py new file mode 100755 index 0000000000000000000000000000000000000000..a2f7c0875133fee61a6342f3a851290355df83f4 --- /dev/null +++ b/verify_jar.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 + +import os +import subprocess +import sys +from utils import calc_hash, fail + + +def main(): + if len(sys.argv) != 3: + fail("Usage: %s reference_jar built_jar" % sys.argv[0]) + if not verify_jar(sys.argv[1], sys.argv[2]): sys.exit(1) + + +def verify_jar(reference_jar, built_jar): + if not os.path.isfile(reference_jar): + sys.stderr.write("Cannot verify: Reference jar '%s' does not exist" % reference_jar) + return False + + if not os.path.isfile(built_jar): + sys.stderr.write("Cannot verify: Built jar '%s' does not exist" % built_jar) + return False + + if calc_hash(reference_jar) != calc_hash(built_jar): + sys.stderr.write("Mismatch detected. Trying to find reason...\n") + subprocess.call(['diffoscope', reference_jar, built_jar]) + sys.stderr.write("Files do NOT match! :(") + return False + else: + print("Files match! :)") + return True + + +if __name__ == "__main__": + main()