diff --git a/contrib/guix/guix-codesign b/contrib/guix/guix-codesign index 4694209e00d..dedee135b4a 100755 --- a/contrib/guix/guix-codesign +++ b/contrib/guix/guix-codesign @@ -137,7 +137,7 @@ fi ################ -# Unsigned tarballs SHOULD exist +# Codesigning tarballs SHOULD exist ################ # Usage: outdir_for_host HOST SUFFIX @@ -149,13 +149,13 @@ outdir_for_host() { } -unsigned_tarball_for_host() { +codesigning_tarball_for_host() { case "$1" in *mingw*) - echo "$(outdir_for_host "$1")/${DISTNAME}-win64-unsigned.tar.gz" + echo "$(outdir_for_host "$1")/${DISTNAME}-win64-codesigning.tar.gz" ;; *darwin*) - echo "$(outdir_for_host "$1")/${DISTNAME}-${1}-unsigned.tar.gz" + echo "$(outdir_for_host "$1")/${DISTNAME}-${1}-codesigning.tar.gz" ;; *) exit 1 @@ -164,22 +164,22 @@ unsigned_tarball_for_host() { } # Accumulate a list of build directories that already exist... -hosts_unsigned_tarball_missing="" +hosts_codesigning_tarball_missing="" for host in $HOSTS; do - if [ ! -e "$(unsigned_tarball_for_host "$host")" ]; then - hosts_unsigned_tarball_missing+=" ${host}" + if [ ! -e "$(codesigning_tarball_for_host "$host")" ]; then + hosts_codesigning_tarball_missing+=" ${host}" fi done -if [ -n "$hosts_unsigned_tarball_missing" ]; then +if [ -n "$hosts_codesigning_tarball_missing" ]; then # ...so that we can print them out nicely in an error message cat << EOF -ERR: Unsigned tarballs do not exist +ERR: Codesigning tarballs do not exist ... EOF -for host in $hosts_unsigned_tarball_missing; do - echo " ${host} '$(unsigned_tarball_for_host "$host")'" +for host in $hosts_codesigning_tarball_missing; do + echo " ${host} '$(codesigning_tarball_for_host "$host")'" done exit 1 fi @@ -371,7 +371,7 @@ EOF OUTDIR="$(OUTDIR_BASE=/outdir-base && outdir_for_host "$HOST" codesigned)" \ DIST_ARCHIVE_BASE=/outdir-base/dist-archive \ DETACHED_SIGS_REPO=/detached-sigs \ - UNSIGNED_TARBALL="$(OUTDIR_BASE=/outdir-base && unsigned_tarball_for_host "$HOST")" \ + CODESIGNING_TARBALL="$(OUTDIR_BASE=/outdir-base && codesigning_tarball_for_host "$HOST")" \ bash -c "cd /bitcoin && bash contrib/guix/libexec/codesign.sh" ) diff --git a/contrib/guix/libexec/build.sh b/contrib/guix/libexec/build.sh index fa6933b3328..d60462d8a0c 100755 --- a/contrib/guix/libexec/build.sh +++ b/contrib/guix/libexec/build.sh @@ -289,24 +289,6 @@ mkdir -p "$DISTSRC" ;; esac - case "$HOST" in - *darwin*) - make deploydir ${V:+V=1} - mkdir -p "unsigned-app-${HOST}" - cp --target-directory="unsigned-app-${HOST}" \ - contrib/macdeploy/detached-sig-create.sh - mv --target-directory="unsigned-app-${HOST}" dist - ( - cd "unsigned-app-${HOST}" - find . -print0 \ - | sort --zero-terminated \ - | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ - | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" \ - || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" && exit 1 ) - ) - make deploy ${V:+V=1} OSX_ZIP="${OUTDIR}/${DISTNAME}-${HOST}-unsigned.zip" - ;; - esac ( cd installed @@ -339,7 +321,7 @@ mkdir -p "$DISTSRC" cp -r "${DISTSRC}/share/rpcauth" "${DISTNAME}/share/" - # Finally, deterministically produce {non-,}debug binary tarballs ready + # Deterministically produce {non-,}debug binary tarballs ready # for release case "$HOST" in *mingw*) @@ -347,8 +329,8 @@ mkdir -p "$DISTSRC" | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" find "${DISTNAME}" -not -name "*.dbg" \ | sort \ - | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" \ - || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" && exit 1 ) + | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-unsigned.zip" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}-unsigned.zip" && exit 1 ) find "${DISTNAME}" -name "*.dbg" -print0 \ | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" find "${DISTNAME}" -name "*.dbg" \ @@ -372,12 +354,13 @@ mkdir -p "$DISTSRC" find "${DISTNAME}" -print0 \ | sort --zero-terminated \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ - | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ - || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-unsigned.tar.gz" && exit 1 ) ;; esac ) # $DISTSRC/installed + # Finally make tarballs for codesigning case "$HOST" in *mingw*) cp -rf --target-directory=. contrib/windeploy @@ -385,13 +368,33 @@ mkdir -p "$DISTSRC" cd ./windeploy mkdir -p unsigned cp --target-directory=unsigned/ "${OUTDIR}/${DISTNAME}-win64-setup-unsigned.exe" + cp -r --target-directory=unsigned/ "${INSTALLPATH}" + find unsigned/ -name "*.dbg" -print0 \ + | xargs -0r rm find . -print0 \ | sort --zero-terminated \ | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ - | gzip -9n > "${OUTDIR}/${DISTNAME}-win64-unsigned.tar.gz" \ - || ( rm -f "${OUTDIR}/${DISTNAME}-win64-unsigned.tar.gz" && exit 1 ) + | gzip -9n > "${OUTDIR}/${DISTNAME}-win64-codesigning.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-win64-codesigning.tar.gz" && exit 1 ) ) ;; + *darwin*) + make deploydir ${V:+V=1} + mkdir -p "unsigned-app-${HOST}" + cp --target-directory="unsigned-app-${HOST}" \ + contrib/macdeploy/detached-sig-create.sh + mv --target-directory="unsigned-app-${HOST}" dist + cp -r --target-directory="unsigned-app-${HOST}" "${INSTALLPATH}" + ( + cd "unsigned-app-${HOST}" + find . -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}-codesigning.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}-codesigning.tar.gz" && exit 1 ) + ) + make deploy ${V:+V=1} OSX_ZIP="${OUTDIR}/${DISTNAME}-${HOST}-unsigned.zip" + ;; esac ) # $DISTSRC diff --git a/contrib/guix/libexec/codesign.sh b/contrib/guix/libexec/codesign.sh index b56d2a23094..3a729371114 100755 --- a/contrib/guix/libexec/codesign.sh +++ b/contrib/guix/libexec/codesign.sh @@ -4,6 +4,9 @@ # file COPYING or http://www.opensource.org/licenses/mit-license.php. export LC_ALL=C set -e -o pipefail + +# Environment variables for determinism +export TAR_OPTIONS="--owner=0 --group=0 --numeric-owner --mtime='@${SOURCE_DATE_EPOCH}' --sort=name" export TZ=UTC # Although Guix _does_ set umask when building its own packages (in our case, @@ -27,7 +30,7 @@ fi # Check that required environment variables are set cat << EOF Required environment variables as seen inside the container: - UNSIGNED_TARBALL: ${UNSIGNED_TARBALL:?not set} + CODESIGNING_TARBALL: ${CODESIGNING_TARBALL:?not set} DETACHED_SIGS_REPO: ${DETACHED_SIGS_REPO:?not set} DIST_ARCHIVE_BASE: ${DIST_ARCHIVE_BASE:?not set} DISTNAME: ${DISTNAME:?not set} @@ -63,27 +66,54 @@ mkdir -p "$DISTSRC" ( cd "$DISTSRC" - tar -xf "$UNSIGNED_TARBALL" + tar -xf "$CODESIGNING_TARBALL" mkdir -p codesignatures tar -C codesignatures -xf "$CODESIGNATURE_GIT_ARCHIVE" case "$HOST" in *mingw*) - find "$PWD" -name "*-unsigned.exe" | while read -r infile; do - infile_base="$(basename "$infile")" - - # Codesigned *-unsigned.exe and output to OUTDIR + # Apply detached codesignatures + WORKDIR=".tmp" + mkdir -p ${WORKDIR} + cp -r --target-directory="${WORKDIR}" "unsigned/${DISTNAME}" + find "${WORKDIR}/${DISTNAME}" -name "*.exe" -type f -exec rm {} \; + find unsigned/ -name "*.exe" -type f | while read -r bin + do + bin_base="$(realpath --relative-to=unsigned/ "${bin}")" + mkdir -p "${WORKDIR}/$(dirname "${bin_base}")" osslsigncode attach-signature \ - -in "$infile" \ - -out "${OUTDIR}/${infile_base/-unsigned}" \ + -in "${bin}" \ + -out "${WORKDIR}/${bin_base/-unsigned}" \ -CAfile "$GUIX_ENVIRONMENT/etc/ssl/certs/ca-certificates.crt" \ - -sigin codesignatures/win/"$infile_base".pem + -sigin codesignatures/win/"${bin_base}".pem done + + # Move installer to outdir + cd "${WORKDIR}" + find . -name "*setup.exe" -print0 \ + | xargs -0r mv --target-directory="${OUTDIR}" + + # Make .zip from binaries + find "${DISTNAME}" -print0 \ + | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" + find "${DISTNAME}" \ + | sort \ + | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST//x86_64-w64-mingw32/win64}.zip" && exit 1 ) ;; *darwin*) - # Apply detached codesignatures to dist/ (in-place) - signapple apply dist/Bitcoin-Qt.app codesignatures/osx/dist + case "$HOST" in + arm64*) ARCH="arm64" ;; + x86_64*) ARCH="x86_64" ;; + esac + + # Apply detached codesignatures (in-place) + signapple apply dist/Bitcoin-Qt.app codesignatures/osx/"${HOST}"/dist/Bitcoin-Qt.app + find "${DISTNAME}" -wholename "*/bin/*" -type f | while read -r bin + do + signapple apply "${bin}" "codesignatures/osx/${HOST}/${bin}.${ARCH}sign" + done # Make a .zip from dist/ cd dist/ @@ -91,6 +121,14 @@ mkdir -p "$DISTSRC" | xargs -0r touch --no-dereference --date="@${SOURCE_DATE_EPOCH}" find . | sort \ | zip -X@ "${OUTDIR}/${DISTNAME}-${HOST}.zip" + cd .. + + # Make a .tar.gz from bins + find "${DISTNAME}" -print0 \ + | sort --zero-terminated \ + | tar --create --no-recursion --mode='u+rw,go+r-w,a+X' --null --files-from=- \ + | gzip -9n > "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" \ + || ( rm -f "${OUTDIR}/${DISTNAME}-${HOST}.tar.gz" && exit 1 ) ;; *) exit 1 @@ -105,7 +143,7 @@ mv --no-target-directory "$OUTDIR" "$ACTUAL_OUTDIR" \ ( cd /outdir-base { - echo "$UNSIGNED_TARBALL" + echo "$CODESIGNING_TARBALL" echo "$CODESIGNATURE_GIT_ARCHIVE" find "$ACTUAL_OUTDIR" -type f } | xargs realpath --relative-base="$PWD" \ diff --git a/contrib/guix/manifest.scm b/contrib/guix/manifest.scm index 1a47e91b493..d37a76f64cc 100644 --- a/contrib/guix/manifest.scm +++ b/contrib/guix/manifest.scm @@ -17,13 +17,14 @@ (gnu packages moreutils) (gnu packages pkg-config) ((gnu packages python) #:select (python-minimal)) - ((gnu packages python-build) #:select (python-tomli)) + ((gnu packages python-build) #:select (python-tomli python-poetry-core)) ((gnu packages python-crypto) #:select (python-asn1crypto)) ((gnu packages tls) #:select (openssl)) ((gnu packages version-control) #:select (git-minimal)) (guix build-system cmake) (guix build-system gnu) (guix build-system python) + (guix build-system pyproject) (guix build-system trivial) (guix download) (guix gexp) @@ -393,10 +394,10 @@ specific moment in time, whitelisting and revocation checks.") (license license:expat)))) (define-public python-signapple - (let ((commit "62155712e7417aba07565c9780a80e452823ae6a")) + (let ((commit "85bfcecc33d2773bc09bc318cec0614af2c8e287")) (package (name "python-signapple") - (version (git-version "0.1" "1" commit)) + (version (git-version "0.2.0" "1" commit)) (source (origin (method git-fetch) @@ -406,13 +407,14 @@ specific moment in time, whitelisting and revocation checks.") (file-name (git-file-name name commit)) (sha256 (base32 - "1nm6rm4h4m7kbq729si4cm8rzild62mk4ni8xr5zja7l33fhv3gb")))) - (build-system python-build-system) + "17yqjll8nw83q6dhgqhkl7w502z5vy9sln8m6mlx0f1c10isg8yg")))) + (build-system pyproject-build-system) (propagated-inputs (list python-asn1crypto python-oscrypto python-certvalidator python-elfesteem)) + (native-inputs (list python-poetry-core)) ;; There are no tests, but attempting to run python setup.py test leads to ;; problems, just disable the test (arguments '(#:tests? #f)) diff --git a/contrib/macdeploy/detached-sig-create.sh b/contrib/macdeploy/detached-sig-create.sh index 097a7c35ee4..89094403b7f 100755 --- a/contrib/macdeploy/detached-sig-create.sh +++ b/contrib/macdeploy/detached-sig-create.sh @@ -6,26 +6,57 @@ export LC_ALL=C set -e -ROOTDIR=dist -BUNDLE="${ROOTDIR}/Bitcoin-Qt.app" -BINARY="${BUNDLE}/Contents/MacOS/Bitcoin-Qt" SIGNAPPLE=signapple TEMPDIR=sign.temp -ARCH=$(${SIGNAPPLE} info ${BINARY} | head -n 1 | cut -d " " -f 1) -OUT="signature-osx-${ARCH}.tar.gz" -OUTROOT=osx/dist -if [ -z "$1" ]; then - echo "usage: $0 " - echo "example: $0 " +BUNDLE_ROOT=dist +BUNDLE_NAME="Bitcoin-Qt.app" +UNSIGNED_BUNDLE="${BUNDLE_ROOT}/${BUNDLE_NAME}" +UNSIGNED_BINARY="${UNSIGNED_BUNDLE}/Contents/MacOS/Bitcoin-Qt" + +ARCH=$(${SIGNAPPLE} info ${UNSIGNED_BINARY} | head -n 1 | cut -d " " -f 1) + +OUTDIR="osx/${ARCH}-apple-darwin" +OUTROOT="${TEMPDIR}/${OUTDIR}" + +OUT="signature-osx-${ARCH}.tar.gz" + +if [ "$#" -ne 3 ]; then + echo "usage: $0 " exit 1 fi rm -rf ${TEMPDIR} mkdir -p ${TEMPDIR} -${SIGNAPPLE} sign -f --detach "${TEMPDIR}/${OUTROOT}" "$@" "${BUNDLE}" --hardened-runtime +stty -echo +printf "Enter the passphrase for %s: " "$1" +read cs_key_pass +printf "\n" +printf "Enter the passphrase for %s: " "$2" +read api_key_pass +printf "\n" +stty echo -tar -C "${TEMPDIR}" -czf "${OUT}" . +# Sign and notarize app bundle +${SIGNAPPLE} sign -f --hardened-runtime --detach "${OUTROOT}/${BUNDLE_ROOT}" --passphrase "${cs_key_pass}" "$1" "${UNSIGNED_BUNDLE}" +${SIGNAPPLE} apply "${UNSIGNED_BUNDLE}" "${OUTROOT}/${BUNDLE_ROOT}/${BUNDLE_NAME}" +${SIGNAPPLE} notarize --detach "${OUTROOT}/${BUNDLE_ROOT}" --passphrase "${api_key_pass}" "$2" "$3" "${UNSIGNED_BUNDLE}" + +# Sign each binary +find . -maxdepth 3 -wholename "*/bin/*" -type f -exec realpath --relative-to=. {} \; | while read -r bin +do + bin_dir=$(dirname "${bin}") + bin_name=$(basename "${bin}") + ${SIGNAPPLE} sign -f --hardened-runtime --detach "${OUTROOT}/${bin_dir}" --passphrase "${cs_key_pass}" "$1" "${bin}" + ${SIGNAPPLE} apply "${bin}" "${OUTROOT}/${bin_dir}/${bin_name}.${ARCH}sign" +done + +# Notarize the binaries +# Binaries cannot have stapled notarizations so this does not actually generate any output +binaries_dir=$(dirname "$(find . -maxdepth 2 -wholename '*/bin' -type d -exec realpath --relative-to=. {} \;)") +${SIGNAPPLE} notarize --passphrase "${api_key_pass}" "$2" "$3" "${binaries_dir}" + +tar -C "${TEMPDIR}" -czf "${OUT}" "${OUTDIR}" rm -rf "${TEMPDIR}" echo "Created ${OUT}" diff --git a/doc/release-notes.md b/doc/release-notes.md index 5dcf6b07f5a..a9cb656435e 100644 --- a/doc/release-notes.md +++ b/doc/release-notes.md @@ -25,13 +25,6 @@ Upgrading directly from a version of Bitcoin Core that has reached its EOL is possible, but it might take some time if the data directory needs to be migrated. Old wallet versions of Bitcoin Core are generally supported. -Running Bitcoin Core binaries on macOS requires self signing. -``` -cd /path/to/bitcoin-28.x/bin -xattr -d com.apple.quarantine bitcoin-cli bitcoin-qt bitcoin-tx bitcoin-util bitcoin-wallet bitcoind test_bitcoin -codesign -s - bitcoin-cli bitcoin-qt bitcoin-tx bitcoin-util bitcoin-wallet bitcoind test_bitcoin -``` - Compatibility ============== @@ -46,8 +39,9 @@ Notable changes ### Build -- #31627 depends: Fix spacing issue +- #31407 guix: Notarize MacOS app bundle and codesign all MacOS and Windows binaries - #31500 depends: Fix compiling libevent package on NetBSD +- #31627 depends: Fix spacing issue - #32070 build: use make < 3.82 syntax for define directive ### Test @@ -68,6 +62,7 @@ Credits ======= - 0xB10C +- achow101 - Brandon Odiwuor - Hennadii Stepanov - kehiy diff --git a/doc/release-process.md b/doc/release-process.md index fa2c53eb0c5..ca42c0ff496 100644 --- a/doc/release-process.md +++ b/doc/release-process.md @@ -164,8 +164,8 @@ Then open a Pull Request to the [guix.sigs repository](https://github.com/bitcoi In the `guix-build-${VERSION}/output/x86_64-apple-darwin` and `guix-build-${VERSION}/output/arm64-apple-darwin` directories: - tar xf bitcoin-osx-unsigned.tar.gz - ./detached-sig-create.sh /path/to/codesign.p12 + tar xf bitcoin-${VERSION}-${ARCH}-apple-darwin-codesigning.tar.gz + ./detached-sig-create.sh /path/to/codesign.p12 /path/to/AuthKey_foo.p8 uuid Enter the keychain password and authorize the signature signature-osx.tar.gz will be created @@ -173,7 +173,7 @@ In the `guix-build-${VERSION}/output/x86_64-apple-darwin` and `guix-build-${VERS In the `guix-build-${VERSION}/output/x86_64-w64-mingw32` directory: - tar xf bitcoin-win-unsigned.tar.gz + tar xf bitcoin-${VERSION}-win64-codesigning.tar.gz ./detached-sig-create.sh -key /path/to/codesign.key Enter the passphrase for the key when prompted signature-win.tar.gz will be created