1#!/usr/bin/env bash
2# Copyright (c) 2016-2019 The Bitcoin Core developers
3# Distributed under the MIT software license, see the accompanying
4# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6###   This script attempts to download the signature file SHA256SUMS.asc from
7###   bitcoincore.org and bitcoin.org and compares them.
8###   It first checks if the signature passes, and then downloads the files specified in
9###   the file, and checks if the hashes of these files match those that are specified
10###   in the signature file.
11###   The script returns 0 if everything passes the checks. It returns 1 if either the
12###   signature check or the hash check doesn't pass. If an error occurs the return value is 2
13
14export LC_ALL=C
15function clean_up {
16   for file in "$@"
17   do
18      rm "$file" 2> /dev/null
19   done
20}
21
22WORKINGDIR="/tmp/bitcoin_verify_binaries"
23TMPFILE="hashes.tmp"
24
25SIGNATUREFILENAME="SHA256SUMS.asc"
26RCSUBDIR="test"
27HOST1="https://bitcoincore.org"
28HOST2="https://bitcoin.org"
29BASEDIR="/bin/"
30VERSIONPREFIX="bitcoin-core-"
31RCVERSIONSTRING="rc"
32
33if [ ! -d "$WORKINGDIR" ]; then
34   mkdir "$WORKINGDIR"
35fi
36
37cd "$WORKINGDIR" || exit 1
38
39#test if a version number has been passed as an argument
40if [ -n "$1" ]; then
41   #let's also check if the version number includes the prefix 'bitcoin-',
42   #  and add this prefix if it doesn't
43   if [[ $1 == "$VERSIONPREFIX"* ]]; then
44      VERSION="$1"
45   else
46      VERSION="$VERSIONPREFIX$1"
47   fi
48
49   STRIPPEDLAST="${VERSION%-*}"
50
51   #now let's see if the version string contains "rc" or a platform name (e.g. "osx")
52   if [[ "$STRIPPEDLAST-" == "$VERSIONPREFIX" ]]; then
53      BASEDIR="$BASEDIR$VERSION/"
54   else
55      # let's examine the last part to see if it's rc and/or platform name
56      STRIPPEDNEXTTOLAST="${STRIPPEDLAST%-*}"
57      if [[ "$STRIPPEDNEXTTOLAST-" == "$VERSIONPREFIX" ]]; then
58
59         LASTSUFFIX="${VERSION##*-}"
60         VERSION="$STRIPPEDLAST"
61
62         if [[ $LASTSUFFIX == *"$RCVERSIONSTRING"* ]]; then
63            RCVERSION="$LASTSUFFIX"
64         else
65            PLATFORM="$LASTSUFFIX"
66         fi
67
68      else
69         RCVERSION="${STRIPPEDLAST##*-}"
70         PLATFORM="${VERSION##*-}"
71
72         VERSION="$STRIPPEDNEXTTOLAST"
73      fi
74
75      BASEDIR="$BASEDIR$VERSION/"
76      if [[ $RCVERSION == *"$RCVERSIONSTRING"* ]]; then
77         BASEDIR="$BASEDIR$RCSUBDIR.$RCVERSION/"
78      fi
79   fi
80else
81   echo "Error: need to specify a version on the command line"
82   exit 2
83fi
84
85if ! WGETOUT=$(wget -N "$HOST1$BASEDIR$SIGNATUREFILENAME" 2>&1); then
86   echo "Error: couldn't fetch signature file. Have you specified the version number in the following format?"
87   # shellcheck disable=SC1087
88   echo "[$VERSIONPREFIX]<version>-[$RCVERSIONSTRING[0-9]] (example: ${VERSIONPREFIX}0.10.4-${RCVERSIONSTRING}1)"
89   echo "wget output:"
90   # shellcheck disable=SC2001
91   echo "$WGETOUT"|sed 's/^/\t/g'
92   exit 2
93fi
94
95if ! WGETOUT=$(wget -N -O "$SIGNATUREFILENAME.2" "$HOST2$BASEDIR$SIGNATUREFILENAME" 2>&1); then
96   echo "bitcoin.org failed to provide signature file, but bitcoincore.org did?"
97   echo "wget output:"
98   # shellcheck disable=SC2001
99   echo "$WGETOUT"|sed 's/^/\t/g'
100   clean_up $SIGNATUREFILENAME
101   exit 3
102fi
103
104SIGFILEDIFFS="$(diff $SIGNATUREFILENAME $SIGNATUREFILENAME.2)"
105if [ "$SIGFILEDIFFS" != "" ]; then
106   echo "bitcoin.org and bitcoincore.org signature files were not equal?"
107   clean_up $SIGNATUREFILENAME $SIGNATUREFILENAME.2
108   exit 4
109fi
110
111#then we check it
112GPGOUT=$(gpg --yes --decrypt --output "$TMPFILE" "$SIGNATUREFILENAME" 2>&1)
113
114#return value 0: good signature
115#return value 1: bad signature
116#return value 2: gpg error
117
118RET="$?"
119if [ $RET -ne 0 ]; then
120   if [ $RET -eq 1 ]; then
121      #and notify the user if it's bad
122      echo "Bad signature."
123   elif [ $RET -eq 2 ]; then
124      #or if a gpg error has occurred
125      echo "gpg error. Do you have the Bitcoin Core binary release signing key installed?"
126   fi
127
128   echo "gpg output:"
129   # shellcheck disable=SC2001
130   echo "$GPGOUT"|sed 's/^/\t/g'
131   clean_up $SIGNATUREFILENAME $SIGNATUREFILENAME.2 $TMPFILE
132   exit "$RET"
133fi
134
135if [ -n "$PLATFORM" ]; then
136   grep $PLATFORM $TMPFILE > "$TMPFILE-plat"
137   TMPFILESIZE=$(stat -c%s "$TMPFILE-plat")
138   if [ $TMPFILESIZE -eq 0 ]; then
139      echo "error: no files matched the platform specified" && exit 3
140   fi
141   mv "$TMPFILE-plat" $TMPFILE
142fi
143
144#here we extract the filenames from the signature file
145FILES=$(awk '{print $2}' "$TMPFILE")
146
147#and download these one by one
148for file in $FILES
149do
150   echo "Downloading $file"
151   wget --quiet -N "$HOST1$BASEDIR$file"
152done
153
154#check hashes
155DIFF=$(diff <(sha256sum $FILES) "$TMPFILE")
156
157if [ $? -eq 1 ]; then
158   echo "Hashes don't match."
159   echo "Offending files:"
160   echo "$DIFF"|grep "^<"|awk '{print "\t"$3}'
161   exit 1
162elif [ $? -gt 1 ]; then
163   echo "Error executing 'diff'"
164   exit 2
165fi
166
167if [ -n "$2" ]; then
168   echo "Clean up the binaries"
169   clean_up $FILES $SIGNATUREFILENAME $SIGNATUREFILENAME.2 $TMPFILE
170else
171   echo "Keep the binaries in $WORKINGDIR"
172   clean_up $TMPFILE
173fi
174
175echo -e "Verified hashes of \n$FILES"
176
177exit 0
178