From 644424296bdaa95c55b6e53a50722bede5993c33 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 12 Feb 2021 10:35:28 +0100 Subject: [PATCH 1/4] scripts: use shasum instead of sha256sum Because the sha256sum binary isn't available on MacOS we instead use the shasum -a 256 command that was used before. --- scripts/release.sh | 4 ++-- scripts/verify-install.sh | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/release.sh b/scripts/release.sh index 6ff4c3b6b..930d680e3 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -183,7 +183,7 @@ function build_release() { # Add the hashes for the individual binaries as well for easy verification # of a single installed binary. - sha256sum "${dir}/"* >> "manifest-$tag.txt" + shasum -a 256 "${dir}/"* >> "manifest-$tag.txt" if [[ $os == "windows" ]]; then reproducible_zip "${dir}" @@ -193,7 +193,7 @@ function build_release() { done # Add the hash of the packages too, then sort by the second column (name). - sha256sum lnd-* vendor* >> "manifest-$tag.txt" + shasum -a 256 lnd-* vendor* >> "manifest-$tag.txt" LC_ALL=C sort -k2 -o "manifest-$tag.txt" "manifest-$tag.txt" cat "manifest-$tag.txt" } diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index e62e8001a..30248cb02 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -63,8 +63,8 @@ check_command gpg LND_VERSION=$($LND_BIN --version | cut -d'=' -f2) LNCLI_VERSION=$($LNCLI_BIN --version | cut -d'=' -f2) -LND_SUM=$(sha256sum $LND_BIN | cut -d' ' -f1) -LNCLI_SUM=$(sha256sum $LNCLI_BIN | cut -d' ' -f1) +LND_SUM=$(shasum -a 256 $LND_BIN | cut -d' ' -f1) +LNCLI_SUM=$(shasum -a 256 $LNCLI_BIN | cut -d' ' -f1) echo "Detected lnd $LND_BIN version $LND_VERSION with SHA256 sum $LND_SUM" echo "Detected lncli $LNCLI_BIN version $LNCLI_VERSION with SHA256 sum $LNCLI_SUM" From 132d23c96452ca1eb1471b4bb0abbe0bcc862b8b Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 12 Feb 2021 10:41:27 +0100 Subject: [PATCH 2/4] scripts: verify hash length To make sure we've actually calculated the hash correctly, we make sure it's 64 characters long. --- scripts/verify-install.sh | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index 30248cb02..cf5f9ad3e 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -81,6 +81,16 @@ if [[ ! "$LND_VERSION" =~ $version_regex ]]; then exit 1 fi +# Make sure the hash was actually calculated by looking at its length. +if [[ ${#LND_SUM} -ne 64 ]]; then + echo "ERROR: Invalid hash for lnd: $LND_SUM!" + exit 1 +fi +if [[ ${#LNCLI_SUM} -ne 64 ]]; then + echo "ERROR: Invalid hash for lncli: $LNCLI_SUM!" + exit 1 +fi + # If we're inside the docker image, there should be a shasums.txt file in the # root directory. If that's the case, we first want to make sure we still have # the same hash as we did when building the image. From 99ba2728221cdd62d3c1f235c98125b1b73a33a0 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Fri, 12 Feb 2021 10:52:28 +0100 Subject: [PATCH 3/4] docs+scripts: switch to detached signatures Due to a misunderstanding of how the gpg command line options work, we didn't actually create detached signatures because the --clear-sign flag would overwrite that. We update our verification script to now only download the detached signatures and verify them against the main manifest file. We also update the signing instructions. --- .github/workflows/release.yaml | 6 ++-- docs/release.md | 16 +++++----- scripts/verify-install.sh | 57 +++++++++++++++++++--------------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 704f406e4..0f40d5209 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -73,10 +73,10 @@ jobs: curl https://keybase.io/roasbeef/pgp_keys.asc | gpg --import ``` - Once you have the required PGP keys, you can verify the release (assuming `manifest-roasbeef-${{ env.RELEASE_VERSION }}.txt.asc` is in the current directory) with: + Once you have the required PGP keys, you can verify the release (assuming `manifest-roasbeef-${{ env.RELEASE_VERSION }}.sig` and `manifest-${{ env.RELEASE_VERSION }}.txt` are in the current directory) with: ``` - gpg --verify manifest-roasbeef-${{ env.RELEASE_VERSION }}.txt.asc + gpg --verify manifest-roasbeef-${{ env.RELEASE_VERSION }}.sig manifest-${{ env.RELEASE_VERSION }}.txt ``` You should see the following if the verification was successful: @@ -95,7 +95,7 @@ jobs: Assuming you have the opentimestamps client installed locally, the timestamps can be verified with the following commands: ``` - ots verify manifest-roasbeef-${{ env.RELEASE_VERSION }}.txt.asc.ots -f manifest-roasbeef-${{ env.RELEASE_VERSION }}.txt.asc + ots verify manifest-roasbeef-${{ env.RELEASE_VERSION }}.sig.ots -f manifest-roasbeef-${{ env.RELEASE_VERSION }}.sig ``` Alternatively, [the open timestamps website](https://opentimestamps.org/) can be used to verify timestamps if one doesn't have a `bitcoind` instance accessible locally. diff --git a/docs/release.md b/docs/release.md index 8b364fda6..42f2b8df3 100644 --- a/docs/release.md +++ b/docs/release.md @@ -99,11 +99,11 @@ script in the image that can be called (before starting the container for example): ```shell -$ docker pull lightninglabs/lnd:v0.12.0-beta -$ docker run --rm --entrypoint="" lightninglabs/lnd:v0.12.0-beta /verify-install.sh -$ OK=$? -$ if [ "$OK" -ne "0" ]; then echo "Verification failed!"; exit 1; done -$ docker run lightninglabs/lnd [command-line options] +⛰ docker pull lightninglabs/lnd:v0.12.0-beta +⛰ docker run --rm --entrypoint="" lightninglabs/lnd:v0.12.0-beta /verify-install.sh +⛰ OK=$? +⛰ if [ "$OK" -ne "0" ]; then echo "Verification failed!"; exit 1; done +⛰ docker run lightninglabs/lnd [command-line options] ``` # Signing an Existing Manifest File @@ -121,8 +121,6 @@ signature during signing. Assuming `USERNAME` is your current nick as a developer, then the following command will generate a proper signature: +```shell +⛰ gpg --detach-sig --output manifest-USERNAME-TAG.sig manifest-TAG.txt ``` -gpg --detach-sig --output manifest-USERNAME-TAG.txt.asc --clear-sign manifest-TAG.txt -``` - - diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index cf5f9ad3e..2bdd18c57 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -5,7 +5,8 @@ PROJECT=lnd RELEASE_URL=https://github.com/$REPO/$PROJECT/releases API_URL=https://api.github.com/repos/$REPO/$PROJECT/releases -SIGNATURE_SELECTOR=". | select(.name | test(\"manifest-.*(\\\\.txt\\\\.asc)$\")) | .name" +MANIFEST_SELECTOR=". | select(.name | test(\"manifest-v.*(\\\\.txt)$\")) | .name" +SIGNATURE_SELECTOR=". | select(.name | test(\"manifest-.*(\\\\.sig)$\")) | .name" HEADER_JSON="Accept: application/json" HEADER_GH_JSON="Accept: application/vnd.github.v3+json" @@ -129,13 +130,17 @@ TAG_NAME=$(echo $RELEASE_JSON | jq -r '.tag_name') RELEASE_ID=$(echo $RELEASE_JSON | jq -r '.id') echo "Release $TAG_NAME found with ID $RELEASE_ID" -# Now download the asset list and filter by manifests and signatures. +# Now download the asset list and filter by the manifest and the signatures. ASSETS=$(curl -L -s -H "$HEADER_GH_JSON" "$API_URL/$RELEASE_ID" | jq -c '.assets[]') +MANIFEST=$(echo $ASSETS | jq -r "$MANIFEST_SELECTOR") SIGNATURES=$(echo $ASSETS | jq -r "$SIGNATURE_SELECTOR") -# Download all "manifest-*.txt.asc" as those contain both the hashes that were -# signed and the signature itself (=detached sig). +# Download the main "manifest-*.txt" and all "manifest-*.sig" files containing +# the detached signatures. TEMP_DIR=$(mktemp -d /tmp/lnd-sig-verification-XXXXXX) +echo "Downloading $MANIFEST" +curl -L -s -o "$TEMP_DIR/$MANIFEST" "$RELEASE_URL/download/$LND_VERSION/$MANIFEST" + for signature in $SIGNATURES; do echo "Downloading $signature" curl -L -s -o "$TEMP_DIR/$signature" "$RELEASE_URL/download/$LND_VERSION/$signature" @@ -144,13 +149,14 @@ done echo "" cd $TEMP_DIR || exit 1 +# Before we even look at the content of the manifest, we first want to make sure +# the signatures actually sign that exact manifest. NUM_CHECKS=0 for signature in $SIGNATURES; do - # First make sure the downloaded signature file is valid. echo "Verifying $signature" - if gpg --verify "$signature" 2>&1 | grep -q "Good signature"; then + if gpg --verify "$signature" "$MANIFEST" 2>&1 | grep -q "Good signature"; then echo "Signature for $signature checks out: " - gpg --verify "$signature" 2>&1 | grep "using" + gpg --verify "$signature" "$MANIFEST" 2>&1 | grep "using" elif gpg --verify "$signature" 2>&1 | grep -q "No public key"; then echo "Unable to verify signature $signature, no key available, skipping" continue @@ -159,32 +165,33 @@ for signature in $SIGNATURES; do exit 1 fi - echo "" - - # Then make sure that the hash of the installed binaries can be found in the - # signed list of hashes. - if ! grep -q "$LND_SUM" "$signature"; then - echo "ERROR: Hash $LND_SUM for lnd not found in $signature: " - cat "$signature" - exit 1 - fi - - if ! grep -q "$LNCLI_SUM" "$signature"; then - echo "ERROR: Hash $LNCLI_SUM for lncli not found in $signature: " - cat "$signature" - exit 1 - fi - - echo "Verified lnd and lncli hashes against $signature" + echo "Verified $signature against $MANIFEST" ((NUM_CHECKS=NUM_CHECKS+1)) done +# Then make sure that the hash of the installed binaries can be found in the +# manifest that we now have verified the signatures for. +if ! grep -q "^$LND_SUM" "$MANIFEST"; then + echo "ERROR: Hash $LND_SUM for lnd not found in $MANIFEST: " + cat "$MANIFEST" + exit 1 +fi + +if ! grep -q "^$LNCLI_SUM" "$MANIFEST"; then + echo "ERROR: Hash $LNCLI_SUM for lncli not found in $MANIFEST: " + cat "$MANIFEST" + exit 1 +fi + +echo "" +echo "Verified lnd and lncli hashes against $MANIFEST" + # We want at least one signature that signs the hashes of the binaries we have # installed. If we arrive here without exiting, it means no signature manifests # were uploaded (yet) with the correct naming pattern. if [[ $NUM_CHECKS -lt 1 ]]; then echo "ERROR: No valid signatures found!" - echo "Make sure the release $LND_VERSION contains any signed manifests." + echo "Make sure the release $LND_VERSION contains any signatures for the manifest." exit 1 fi From 85c42b0b79e000f1e57a207e9c1a6056c0df1d87 Mon Sep 17 00:00:00 2001 From: Oliver Gugger Date: Mon, 15 Feb 2021 10:43:19 +0100 Subject: [PATCH 4/4] scripts: add more verbose error messages to verification We want to be more precise in what exactly went wrong and what the cause could be. --- scripts/verify-install.sh | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/scripts/verify-install.sh b/scripts/verify-install.sh index 2bdd18c57..55fb0703b 100755 --- a/scripts/verify-install.sh +++ b/scripts/verify-install.sh @@ -155,13 +155,16 @@ NUM_CHECKS=0 for signature in $SIGNATURES; do echo "Verifying $signature" if gpg --verify "$signature" "$MANIFEST" 2>&1 | grep -q "Good signature"; then - echo "Signature for $signature checks out: " + echo "Signature for $signature appears valid: " gpg --verify "$signature" "$MANIFEST" 2>&1 | grep "using" elif gpg --verify "$signature" 2>&1 | grep -q "No public key"; then echo "Unable to verify signature $signature, no key available, skipping" continue else - echo "ERROR: Did not get valid signature for $signature!" + echo "ERROR: Did not get valid signature for $MANIFEST in $signature!" + echo " The developer signature $signature disagrees on the expected" + echo " release binaries in $MANIFEST. The release may have been faulty or" + echo " was backdoored." exit 1 fi @@ -174,12 +177,18 @@ done if ! grep -q "^$LND_SUM" "$MANIFEST"; then echo "ERROR: Hash $LND_SUM for lnd not found in $MANIFEST: " cat "$MANIFEST" + echo " The expected release binaries have been verified with the developer " + echo " signatures. Your binary's hash does not match the expected release " + echo " binary hashes. Make sure you're using an official binary." exit 1 fi if ! grep -q "^$LNCLI_SUM" "$MANIFEST"; then echo "ERROR: Hash $LNCLI_SUM for lncli not found in $MANIFEST: " cat "$MANIFEST" + echo " The expected release binaries have been verified with the developer " + echo " signatures. Your binary's hash does not match the expected release " + echo " binary hashes. Make sure you're using an official binary." exit 1 fi @@ -196,4 +205,4 @@ if [[ $NUM_CHECKS -lt 1 ]]; then fi echo "" -echo "SUCCESS! Verified lnd and lncli against $NUM_CHECKS signature(s)." +echo "SUCCESS! Verified lnd and lncli against $NUM_CHECKS developer signature(s)."