First working prototype

FROM debian:stable
ENV DEBIAN_FRONTEND=noninteractive
ENV ANDROID_HOME=/opt/android-sdk
WORKDIR /opt/briar-reproducer
# add required files to WORKDIR
ADD install*.sh ./
ADD ./
ADD ./
RUN ./
CMD ./
#!/usr/bin/env bash
set -e
set -x
# Install Android SDK Manager
wget --no-verbose -O
mv tools ${ANDROID_HOME}/
# Accept all those nasty EULAs
mkdir -p ${ANDROID_HOME}/licenses/
printf "\n8933bad161af4178b1185d1a37fbf41ea5269c55\nd56f5187479451eabf01fb78af6dfcb131a6481e" > ${ANDROID_HOME}/licenses/android-sdk-license
printf "\n84831b9409646a918e30573bab4c9c91346d8abd" > ${ANDROID_HOME}/licenses/android-sdk-preview-license
printf "\n79120722343a6f314e0719f863036c702b0e6b2a\n84831b9409646a918e30573bab4c9c91346d8abd" > ${ANDROID_HOME}/licenses/android-sdk-preview-license-old
# Install platform-tools
mkdir /root/.android
touch /root/.android/repositories.cfg
echo y | $ANDROID_HOME/tools/bin/sdkmanager "platform-tools"
#!/usr/bin/env bash
set -e
set -x
apt-get install -y --no-install-recommends \
enjarify \
python3-pip python3-setuptools python3-wheel \
python3-libarchive-c \
# Install latest diffoscope (version in Debian stable is outdated)
pip3 install diffoscope
# Install latest apktool for verification debugging
wget --no-verbose -O /opt/apktool.jar
cat >/usr/local/bin/apktool <<EOF
#!/usr/bin/env bash
java -jar /opt/apktool.jar \$@
chmod +x /usr/local/bin/apktool
mkdir -p /root/.local/share/apktool/framework
\ No newline at end of file
#!/usr/bin/env bash
set -e
set -x
apt-get install -y --no-install-recommends \
git \
default-jdk-headless \
unzip \
#!/usr/bin/env bash
set -e
set -x
# update package sources
apt-get update
apt-get -y upgrade
# do not install documentation to keep image small
echo "path-exclude=/usr/share/locale/*" >> /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude=/usr/share/man/*" >> /etc/dpkg/dpkg.cfg.d/01_nodoc
echo "path-exclude=/usr/share/doc/*" >> /etc/dpkg/dpkg.cfg.d/01_nodoc
# install dependencies
# clean up for smaller image size
apt-get -y autoremove --purge
apt-get clean
rm -rf /var/lib/apt/lists/*
#!/usr/bin/env python3
import os
import subprocess
import sys
REPO_DIR = "briar"
GRADLE_TASK = "briar-android:assembleRelease"
APK_PATH = "briar-android/build/outputs/apk/release/briar-android-release-unsigned.apk"
def main():
if len(sys.argv) > 2:
fail("Usage: %s [tag]" % sys.argv[0])
tag = sys.argv[1] if len(sys.argv) > 1 else None
# clone/reset repo and checkout tag
tag = prepare_repo(tag)
# download reference binary (before building to detect missing file early on)
version = tag.split('-')[1]
url = REFERENCE_URL % version
reference_apk = "briar-%s.apk" % version
subprocess.check_call(['wget', '--no-verbose', url, '-O', reference_apk])
# build the app
repo_call(["./gradlew", GRADLE_TASK])
# check if both APKs match
apk = os.path.join(REPO_DIR, APK_PATH)
if['./', reference_apk, apk]) == 0:
print("Version '%s' was built reproducible! :)" % tag)
def prepare_repo(tag):
if os.path.isdir(REPO_DIR):
# get latest commits and tags from remote
repo_call(['git', 'fetch', 'origin'])
# checkout to latest master HEAD
repo_call(['git', 'checkout', '-f', 'master'])
# clone repo
subprocess.check_call(['git', 'clone', os.environ.get("REPO_URL"), REPO_DIR])
# undo all changes
repo_call(['git', 'reset', '--hard'])
# clean all untracked files and directories (-d) from repo
repo_call(['git', 'clean', '-dffx'])
# use latest tag if none given
if tag is None:
result = subprocess.check_output(['git', 'describe', '--abbrev=0', '--tags'], cwd=REPO_DIR)
tag = result.decode().rstrip() # strip away line-break
# checkout tag
repo_call(['git', 'checkout', tag])
# return the tag that was used for the checkout
return tag
def repo_call(command):
subprocess.check_call(command, cwd=REPO_DIR)
def fail(msg=""):
sys.stderr.write(msg + "\n")
if __name__ == "__main__":
#!/usr/bin/env python3
import hashlib
import os
import re
import subprocess
import sys
import tempfile
import zipfile
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])
def verify(reference_apk, built_apk):
if not os.path.isfile(reference_apk):
fail("Can not verify: Reference APK '%s' does not exists" % reference_apk)
if not os.path.isfile(built_apk):
fail("Can not verify: Built APK '%s' does not exists" % built_apk)
with tempfile.TemporaryDirectory() as tmp_dir:
# repack reference APK
reference_tmp = os.path.join(tmp_dir, os.path.basename(reference_apk))
repack_apk(reference_apk, reference_tmp)
# repack built APK
built_tmp = os.path.join(tmp_dir, os.path.basename(built_apk))
repack_apk(built_apk, built_tmp)
# compare hashes of repacked APKs
if calc_hash(reference_tmp) != calc_hash(built_tmp):
sys.stderr.write("Mismatch detected. Trying to find reason...\n")['diffoscope', reference_tmp, built_tmp])
fail("Files do NOT match! :(")
print("Files match! :)")
def repack_apk(zip_file, new_zip_file):
# open original zip file
with zipfile.ZipFile(zip_file, 'r') as f:
# write to new zip file
with zipfile.ZipFile(new_zip_file, 'w') as tmp:
# go through all files
for info in f.infolist():
if APK_SIG_FILE.match(info.filename):
continue # ignore signatures
info.extra = b'' # reset file metadata
# add file to new zip
def calc_hash(filename, block_size=65536):
sha512 = hashlib.sha512()
with open(filename, 'rb') as f:
for block in iter(lambda:, b''):
return sha512.hexdigest()
def fail(msg=""):
sys.stderr.write(msg + "\n")
if __name__ == "__main__":
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment