Skip to content

Commit 4df8213

Browse files
committed
Merge #9880: Verify Tree-SHA512s in merge commits, enforce sigs are not SHA1
bbd7579 Fix regsig checking for subkey sigs in verify-commits (Matt Corallo) d025bc7 Allow any subkey in verify-commits (Matt Corallo) eddc77a Add comment re: why SHA1 is disabled (Peter Todd) d9c450f Verify Tree-SHA512s in merge commits, enforce sigs are not SHA1 (Matt Corallo) be908a6 Fail merge if there are any symlinks (Matt Corallo) Tree-SHA512: bb66c59cc1c6b1c86d7d8be7adb0769c6598c0e28ad927409941f30af87d390521e82fc13700ee22e92db1bd571db3e19a152ec7b2c0349c6e06f5de62c0b65f
2 parents 8a3b075 + bbd7579 commit 4df8213

File tree

5 files changed

+101
-12
lines changed

5 files changed

+101
-12
lines changed

contrib/devtools/github-merge.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,14 @@ def ask_prompt(text):
7070
print("",file=stderr)
7171
return reply
7272

73+
def get_symlink_files():
74+
files = sorted(subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', 'HEAD']).splitlines())
75+
ret = []
76+
for f in files:
77+
if (int(f.decode('utf-8').split(" ")[0], 8) & 0o170000) == 0o120000:
78+
ret.append(f.decode('utf-8').split("\t")[1])
79+
return ret
80+
7381
def tree_sha512sum():
7482
files = sorted(subprocess.check_output([GIT, 'ls-tree', '--full-tree', '-r', '--name-only', 'HEAD']).splitlines())
7583
overall = hashlib.sha512()
@@ -200,6 +208,12 @@ def main():
200208
print("ERROR: Creating merge failed (already merged?).",file=stderr)
201209
exit(4)
202210

211+
symlink_files = get_symlink_files()
212+
for f in symlink_files;
213+
print("ERROR: File %s was a symlink" % f)
214+
if len(symlink_files) > 0:
215+
exit(4)
216+
203217
# Put tree SHA512 into the message
204218
try:
205219
first_sha512 = tree_sha512sum()

contrib/verify-commits/gpg.sh

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,21 +8,31 @@ VALID=false
88
REVSIG=false
99
IFS='
1010
'
11-
for LINE in $(echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null); do
11+
if [ "$BITCOIN_VERIFY_COMMITS_ALLOW_SHA1" = 1 ]; then
12+
GPG_RES="$(echo "$INPUT" | gpg --trust-model always "$@" 2>/dev/null)"
13+
else
14+
# Note how we've disabled SHA1 with the --weak-digest option, disabling
15+
# signatures - including selfsigs - that use SHA1. While you might think that
16+
# collision attacks shouldn't be an issue as they'd be an attack on yourself,
17+
# in fact because what's being signed is a commit object that's
18+
# semi-deterministically generated by untrusted input (the pull-req) in theory
19+
# an attacker could construct a pull-req that results in a commit object that
20+
# they've created a collision for. Not the most likely attack, but preventing
21+
# it is pretty easy so we do so as a "belt-and-suspenders" measure.
22+
23+
GPG_RES="$(echo "$INPUT" | gpg --trust-model always --weak-digest sha1 "$@" 2>/dev/null)"
24+
fi
25+
for LINE in $(echo "$GPG_RES"); do
1226
case "$LINE" in
1327
"[GNUPG:] VALIDSIG "*)
1428
while read KEY; do
15-
case "$LINE" in "[GNUPG:] VALIDSIG $KEY "*) VALID=true;; esac
29+
[ "${LINE#?GNUPG:? VALIDSIG * * * * * * * * * }" = "$KEY" ] && VALID=true
1630
done < ./contrib/verify-commits/trusted-keys
1731
;;
1832
"[GNUPG:] REVKEYSIG "*)
1933
[ "$BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG" != 1 ] && exit 1
20-
while read KEY; do
21-
case "$LINE" in "[GNUPG:] REVKEYSIG ${KEY#????????????????????????} "*)
22-
REVSIG=true
23-
GOODREVSIG="[GNUPG:] GOODSIG ${KEY#????????????????????????} "
24-
esac
25-
done < ./contrib/verify-commits/trusted-keys
34+
REVSIG=true
35+
GOODREVSIG="[GNUPG:] GOODSIG ${LINE#* * *}"
2636
;;
2737
esac
2838
done

contrib/verify-commits/trusted-keys

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
71A3B16735405025D447E8F274810B012346C9A6
2-
3F1888C6DCA92A6499C4911FDBA1A67379A1A931
2+
133EAC179436F14A5CF1B794860FEB804E669320
33
32EE5C4C3FA15CCADB46ABE529D4BCB6416F53EC
4-
FE09B823E6D83A3BC7983EAA2D7F2372E50FE137
4+
B8B3F1C0E58C15DB6A81D30C3648A882F4316B9B
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
b00ba6251f71fa1edaabdf809514e1bc3c67862e

contrib/verify-commits/verify-commits.sh

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,26 +9,84 @@
99
DIR=$(dirname "$0")
1010
[ "/${DIR#/}" != "$DIR" ] && DIR=$(dirname "$(pwd)/$0")
1111

12+
echo "Using verify-commits data from ${DIR}"
13+
1214
VERIFIED_ROOT=$(cat "${DIR}/trusted-git-root")
15+
VERIFIED_SHA512_ROOT=$(cat "${DIR}/trusted-sha512-root-commit")
1316
REVSIG_ALLOWED=$(cat "${DIR}/allow-revsig-commits")
1417

1518
HAVE_FAILED=false
1619
IS_SIGNED () {
1720
if [ $1 = $VERIFIED_ROOT ]; then
1821
return 0;
1922
fi
23+
24+
VERIFY_TREE=$2
25+
NO_SHA1=$3
26+
if [ $1 = $VERIFIED_SHA512_ROOT ]; then
27+
if [ "$VERIFY_TREE" = "1" ]; then
28+
echo "All Tree-SHA512s matched up to $VERIFIED_SHA512_ROOT" > /dev/stderr
29+
fi
30+
VERIFY_TREE=0
31+
NO_SHA1=0
32+
fi
33+
34+
if [ "$NO_SHA1" = "1" ]; then
35+
export BITCOIN_VERIFY_COMMITS_ALLOW_SHA1=0
36+
else
37+
export BITCOIN_VERIFY_COMMITS_ALLOW_SHA1=1
38+
fi
39+
2040
if [ "${REVSIG_ALLOWED#*$1}" != "$REVSIG_ALLOWED" ]; then
2141
export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=1
2242
else
2343
export BITCOIN_VERIFY_COMMITS_ALLOW_REVSIG=0
2444
fi
45+
2546
if ! git -c "gpg.program=${DIR}/gpg.sh" verify-commit $1 > /dev/null 2>&1; then
2647
return 1;
2748
fi
49+
50+
if [ "$VERIFY_TREE" = 1 ]; then
51+
IFS_CACHE="$IFS"
52+
IFS='
53+
'
54+
for LINE in $(git ls-tree --full-tree -r $1); do
55+
case "$LINE" in
56+
"12"*)
57+
echo "Repo contains symlinks" > /dev/stderr
58+
IFS="$IFS_CACHE"
59+
return 1
60+
;;
61+
esac
62+
done
63+
IFS="$IFS_CACHE"
64+
65+
FILE_HASHES=""
66+
for FILE in $(git ls-tree --full-tree -r --name-only $1 | LANG=C sort); do
67+
HASH=$(git cat-file blob $1:"$FILE" | sha512sum | { read FIRST OTHER; echo $FIRST; } )
68+
[ "$FILE_HASHES" != "" ] && FILE_HASHES="$FILE_HASHES"$'\n'
69+
FILE_HASHES="$FILE_HASHES$HASH $FILE"
70+
done
71+
HASH_MATCHES=0
72+
MSG="$(git show -s --format=format:%B $1 | tail -n1)"
73+
74+
case "$MSG -" in
75+
"Tree-SHA512: $(echo "$FILE_HASHES" | sha512sum)")
76+
HASH_MATCHES=1;;
77+
esac
78+
79+
if [ "$HASH_MATCHES" = "0" ]; then
80+
echo "Tree-SHA512 did not match for commit $1" > /dev/stderr
81+
HAVE_FAILED=true
82+
return 1
83+
fi
84+
fi
85+
2886
local PARENTS
2987
PARENTS=$(git show -s --format=format:%P $1)
3088
for PARENT in $PARENTS; do
31-
if IS_SIGNED $PARENT; then
89+
if IS_SIGNED $PARENT $VERIFY_TREE $NO_SHA1; then
3290
return 0;
3391
fi
3492
break
@@ -50,7 +108,13 @@ else
50108
TEST_COMMIT="$1"
51109
fi
52110

53-
IS_SIGNED "$TEST_COMMIT"
111+
DO_CHECKOUT_TEST=0
112+
if [ x"$2" = "x--tree-checks" ]; then
113+
DO_CHECKOUT_TEST=1
114+
115+
fi
116+
117+
IS_SIGNED "$TEST_COMMIT" "$DO_CHECKOUT_TEST" 1
54118
RES=$?
55119
if [ "$RES" = 1 ]; then
56120
if ! "$HAVE_FAILED"; then

0 commit comments

Comments
 (0)