mirror of
https://github.com/vitorpamplona/amethyst.git
synced 2026-10-29 23:27:44 +00:00
- Structured Append: stop enforcing the parity byte. Generators disagree on
it, and a mismatch threw away a fully captured code and left the progress
at "0 of 0" (the accumulator had already reset). The conflicting-index
restart still prevents splicing two posters.
- Camera: feed only one sequence's parts per frame (the one being collected,
else the first in view's); two multi-part codes in view no longer restart
each other forever.
- A code read alone is only taken once a thorough pass agrees it is alone, so
the very first fast frame can no longer pick one of two codes before a
thorough pass has ever seen the harder one (at most ~170 ms added).
- Images: one shared ImageCodesOutcome decision for both image paths. One code
next to part of a multi-part code goes to the chooser with a note instead of
opening silently; the partial-progress plural uses the total, like the live
hint ("1 of 3 parts", not "1 of 3 part").
- Image decode caps total pixels (16 MP) instead of the longest edge, so long
scrolling screenshots get their full-resolution pass again.
- One routeForScannedPayload for every entry point, so the never-route-a-hex-
key rule cannot be skipped; removed the unreachable HexKey arm in the sheet.
- verify-reproducible.sh no longer treats the remapped /build/ prefix as a
host-path leak.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0114EYb9yjQ5MhqAmBwHzD6q
129 lines
5.4 KiB
Bash
Executable File
129 lines
5.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#
|
|
# Verify that libzxingcpp_android.so builds reproducibly.
|
|
#
|
|
# Builds the native library twice from a clean state into scratch directories
|
|
# and confirms the two outputs are byte-for-byte identical, then compares them
|
|
# against what is committed under jniLibs. Both builds compile in the canonical
|
|
# path, so a match here means any checkout -- ours, F-Droid's, an auditor's --
|
|
# produces the same bytes. See README.md -> "Reproducible builds".
|
|
#
|
|
# The second build runs against a copy of the NDK in a different directory. Two
|
|
# builds from the same NDK path cannot see a dependency on where the NDK lives,
|
|
# and that is exactly how the build-id leak got through: lld hashed debug info
|
|
# that named the NDK's install path, so F-Droid's rebuild differed from ours in
|
|
# 20 bytes while this script reported a perfect match. Each output is also
|
|
# checked for absolute paths, which is what such a leak usually looks like.
|
|
#
|
|
# Usage:
|
|
# ./verify-reproducible.sh # every ABI
|
|
# ./verify-reproducible.sh --abi arm64-v8a # one ABI (faster)
|
|
#
|
|
# Exit 0 = reproducible and matching the commit, exit 1 = they differ.
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
|
JNILIBS="$PROJECT_ROOT/amethyst/src/main/jniLibs"
|
|
LIB_NAME="libzxingcpp_android.so"
|
|
BUILD_ROOT="${ZXING_REPRO_DIR:-/tmp/amethyst-zxingcpp-build}"
|
|
|
|
# Only the ABIs this run actually rebuilds. Hashing the whole tree would let an
|
|
# untouched ABI hash identically in both runs and report the lot reproducible --
|
|
# the same trap tools/arti-build hit and documents.
|
|
ABIS=(arm64-v8a armeabi-v7a x86 x86_64)
|
|
PASSTHRU=()
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--abi)
|
|
[ $# -ge 2 ] && [ -n "$2" ] || { echo "--abi needs a value" >&2; exit 2; }
|
|
ABIS=("$2"); PASSTHRU+=("$1" "$2"); shift 2 ;;
|
|
*) echo "Unknown argument: $1 (only --abi is supported here)" >&2; exit 2 ;;
|
|
esac
|
|
done
|
|
|
|
sha256() {
|
|
if command -v sha256sum >/dev/null 2>&1; then sha256sum "$@"; else shasum -a 256 "$@"; fi
|
|
}
|
|
|
|
hashes_in() {
|
|
local dir="$1" abi
|
|
# `if`, not `[ -f ] && sha256`: the latter returns 1 for a missing last ABI, and under set -e
|
|
# that aborted the caller's $(...) with no message at all.
|
|
( cd "$dir" && for abi in "${ABIS[@]}"; do
|
|
if [ -f "$abi/$LIB_NAME" ]; then sha256 "$abi/$LIB_NAME"; fi
|
|
done )
|
|
}
|
|
|
|
NDK="$("$SCRIPT_DIR/build-zxingcpp.sh" --print-ndk)"
|
|
|
|
RUN_A="$(mktemp -d -t zxingcpp-verify-a.XXXXXX)"
|
|
RUN_B="$(mktemp -d -t zxingcpp-verify-b.XXXXXX)"
|
|
# Next to the real NDK so it can be hard-linked (instant, no extra space); a plain copy into the
|
|
# scratch dir is the fallback when that directory is not writable or on another filesystem.
|
|
RELOCATED_NDK="$(dirname "$NDK")/.zxingcpp-verify-ndk.$$"
|
|
trap 'rm -rf "$RUN_A" "$RUN_B" "$RELOCATED_NDK"' EXIT
|
|
|
|
if ! cp -al "$NDK" "$RELOCATED_NDK" 2>/dev/null; then
|
|
rm -rf "$RELOCATED_NDK"
|
|
RELOCATED_NDK="$RUN_B/ndk"
|
|
printf '==> Copying the NDK to a second location (could not hard-link it)\n'
|
|
cp -a "$NDK" "$RELOCATED_NDK"
|
|
fi
|
|
|
|
printf '==> Build 1 of 2 (NDK at %s)\n' "$NDK"
|
|
ANDROID_NDK_HOME="$NDK" "$SCRIPT_DIR/build-zxingcpp.sh" --out "$RUN_A/out" ${PASSTHRU[@]+"${PASSTHRU[@]}"} >/dev/null
|
|
|
|
printf '==> Build 2 of 2 (NDK at %s)\n' "$RELOCATED_NDK"
|
|
ANDROID_NDK_HOME="$RELOCATED_NDK" "$SCRIPT_DIR/build-zxingcpp.sh" --out "$RUN_B/out" ${PASSTHRU[@]+"${PASSTHRU[@]}"} >/dev/null
|
|
|
|
STRINGS_TOOL=""
|
|
for candidate in "$NDK"/toolchains/llvm/prebuilt/*/bin/llvm-strings; do
|
|
[ -x "$candidate" ] && { STRINGS_TOOL="$candidate"; break; }
|
|
done
|
|
[ -n "$STRINGS_TOOL" ] || STRINGS_TOOL="$(command -v strings || true)"
|
|
|
|
if [ -n "$STRINGS_TOOL" ]; then
|
|
LEAKS=""
|
|
for abi in "${ABIS[@]}"; do
|
|
for run in "$RUN_A/out" "$RUN_B/out"; do
|
|
found="$("$STRINGS_TOOL" "$run/$abi/$LIB_NAME" | grep -F -e "$NDK" -e "$RELOCATED_NDK" -e "$BUILD_ROOT" -e "$PROJECT_ROOT" || true)"
|
|
# Plus any other host path. Anchored on real top-level directories rather than "any
|
|
# /x/y", which random bytes in .rodata match -- and never /build/ or /zxing-cpp/, the
|
|
# stable prefixes repro-env.sh rewrites paths to on purpose.
|
|
found="$found$("$STRINGS_TOOL" "$run/$abi/$LIB_NAME" | grep -E '^/(home|Users|root|tmp|private|var|opt|usr|mnt)/' || true)"
|
|
[ -n "$found" ] && LEAKS="$LEAKS\n $abi: $(printf '%s' "$found" | head -3 | tr '\n' ' ')"
|
|
done
|
|
done
|
|
if [ -n "$LEAKS" ]; then
|
|
printf '\nNOT REPRODUCIBLE -- absolute paths in the output:%b\n' "$LEAKS"
|
|
exit 1
|
|
fi
|
|
else
|
|
printf 'warning: no strings tool found; absolute paths not checked\n'
|
|
fi
|
|
|
|
A="$(hashes_in "$RUN_A/out")"
|
|
B="$(hashes_in "$RUN_B/out")"
|
|
|
|
if [ "$A" != "$B" ]; then
|
|
printf '\nNOT REPRODUCIBLE -- the two builds differ:\n'
|
|
diff <(printf '%s\n' "$A") <(printf '%s\n' "$B") || true
|
|
exit 1
|
|
fi
|
|
|
|
printf '\nReproducible: two builds produced identical bytes.\n'
|
|
printf '%s\n' "$A" | sed 's/^/ /'
|
|
|
|
# A reproducible build that does not match the commit is still a problem: it
|
|
# means the shipped library came from something other than this tag.
|
|
COMMITTED="$(hashes_in "$JNILIBS")"
|
|
if [ "$COMMITTED" != "$A" ]; then
|
|
printf '\nWARNING: the committed libraries do NOT match this build.\n'
|
|
diff <(printf '%s\n' "$COMMITTED") <(printf '%s\n' "$A") || true
|
|
printf '\nRun build-zxingcpp.sh and commit the result.\n'
|
|
exit 1
|
|
fi
|
|
|
|
printf '\nCommitted libraries match.\n'
|