| 1 | #!/bin/sh |
| 2 | |
| 3 | test_description='git fast-import --signed-commits=<mode>' |
| 4 | |
| 5 | GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main |
| 6 | |
| 7 | . ./test-lib.sh |
| 8 | . "$TEST_DIRECTORY/lib-gpg.sh" |
| 9 | |
| 10 | test_expect_success 'set up unsigned initial commit and import repo' ' |
| 11 | test_commit first && |
| 12 | git init new |
| 13 | ' |
| 14 | |
| 15 | test_expect_success GPG 'set up OpenPGP signed commit' ' |
| 16 | git checkout -b openpgp-signing main && |
| 17 | echo "Content for OpenPGP signing." >file-sign && |
| 18 | git add file-sign && |
| 19 | git commit -S -m "OpenPGP signed commit" && |
| 20 | OPENPGP_SIGNING=$(git rev-parse --verify openpgp-signing) |
| 21 | ' |
| 22 | |
| 23 | test_expect_success GPG 'import OpenPGP signature with --signed-commits=verbatim' ' |
| 24 | git fast-export --signed-commits=verbatim openpgp-signing >output && |
| 25 | git -C new fast-import --quiet --signed-commits=verbatim <output >log 2>&1 && |
| 26 | IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && |
| 27 | test $OPENPGP_SIGNING = $IMPORTED && |
| 28 | test_must_be_empty log |
| 29 | ' |
| 30 | |
| 31 | test_expect_success GPGSM 'set up X.509 signed commit' ' |
| 32 | git checkout -b x509-signing main && |
| 33 | test_config gpg.format x509 && |
| 34 | test_config user.signingkey $GIT_COMMITTER_EMAIL && |
| 35 | echo "Content for X.509 signing." >file-sign && |
| 36 | git add file-sign && |
| 37 | git commit -S -m "X.509 signed commit" && |
| 38 | X509_SIGNING=$(git rev-parse HEAD) |
| 39 | ' |
| 40 | |
| 41 | test_expect_success GPGSM 'import X.509 signature fails with --signed-commits=abort' ' |
| 42 | git fast-export --signed-commits=verbatim x509-signing >output && |
| 43 | test_must_fail git -C new fast-import --quiet --signed-commits=abort <output |
| 44 | ' |
| 45 | |
| 46 | test_expect_success GPGSM 'import X.509 signature with --signed-commits=warn-verbatim' ' |
| 47 | git -C new fast-import --quiet --signed-commits=warn-verbatim <output >log 2>&1 && |
| 48 | IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) && |
| 49 | test $X509_SIGNING = $IMPORTED && |
| 50 | test_grep "importing a commit signature" log |
| 51 | ' |
| 52 | |
| 53 | test_expect_success GPGSSH 'set up SSH signed commit' ' |
| 54 | git checkout -b ssh-signing main && |
| 55 | test_config gpg.format ssh && |
| 56 | test_config user.signingkey "${GPGSSH_KEY_PRIMARY}" && |
| 57 | echo "Content for SSH signing." >file-sign && |
| 58 | git add file-sign && |
| 59 | git commit -S -m "SSH signed commit" && |
| 60 | SSH_SIGNING=$(git rev-parse HEAD) |
| 61 | ' |
| 62 | |
| 63 | test_expect_success GPGSSH 'strip SSH signature with --signed-commits=strip' ' |
| 64 | git fast-export --signed-commits=verbatim ssh-signing >output && |
| 65 | git -C new fast-import --quiet --signed-commits=strip <output >log 2>&1 && |
| 66 | IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) && |
| 67 | test $SSH_SIGNING != $IMPORTED && |
| 68 | git -C new cat-file commit "$IMPORTED" >actual && |
| 69 | test_grep ! -E "^gpgsig" actual && |
| 70 | test_must_be_empty log |
| 71 | ' |
| 72 | |
| 73 | test_expect_success RUST,GPG 'setup a commit with dual OpenPGP signatures on its SHA-1 and SHA-256 formats' ' |
| 74 | # Create a signed SHA-256 commit |
| 75 | git init --object-format=sha256 explicit-sha256 && |
| 76 | git -C explicit-sha256 config extensions.compatObjectFormat sha1 && |
| 77 | git -C explicit-sha256 checkout -b dual-signed && |
| 78 | test_commit -C explicit-sha256 A && |
| 79 | echo B >explicit-sha256/B && |
| 80 | git -C explicit-sha256 add B && |
| 81 | test_tick && |
| 82 | git -C explicit-sha256 commit -S -m "signed commit" B && |
| 83 | SHA256_B=$(git -C explicit-sha256 rev-parse dual-signed) && |
| 84 | |
| 85 | # Create the corresponding SHA-1 commit |
| 86 | SHA1_B=$(git -C explicit-sha256 rev-parse --output-object-format=sha1 dual-signed) && |
| 87 | |
| 88 | # Check that the resulting SHA-1 commit has both signatures |
| 89 | git -C explicit-sha256 cat-file -p $SHA1_B >out && |
| 90 | test_grep -E "^gpgsig " out && |
| 91 | test_grep -E "^gpgsig-sha256 " out |
| 92 | ' |
| 93 | |
| 94 | test_expect_success RUST,GPG 'strip both OpenPGP signatures with --signed-commits=warn-strip' ' |
| 95 | git -C explicit-sha256 fast-export --signed-commits=verbatim dual-signed >output && |
| 96 | test_grep -E "^gpgsig sha1 openpgp" output && |
| 97 | test_grep -E "^gpgsig sha256 openpgp" output && |
| 98 | git -C new fast-import --quiet --signed-commits=warn-strip <output >log 2>&1 && |
| 99 | git -C new cat-file commit refs/heads/dual-signed >actual && |
| 100 | test_grep ! -E "^gpgsig " actual && |
| 101 | test_grep ! -E "^gpgsig-sha256 " actual && |
| 102 | test_grep "stripping a commit signature" log >out && |
| 103 | test_line_count = 2 out |
| 104 | ' |
| 105 | |
| 106 | for mode in strip-if-invalid sign-if-invalid abort-if-invalid |
| 107 | do |
| 108 | test_expect_success GPG "import commit with no signature with --signed-commits=$mode" ' |
| 109 | git fast-export main >output && |
| 110 | git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && |
| 111 | test_must_be_empty log |
| 112 | ' |
| 113 | |
| 114 | test_expect_success GPG "keep valid OpenPGP signature with --signed-commits=$mode" ' |
| 115 | rm -rf new && |
| 116 | git init new && |
| 117 | |
| 118 | git fast-export --signed-commits=verbatim openpgp-signing >output && |
| 119 | git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && |
| 120 | IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && |
| 121 | test $OPENPGP_SIGNING = $IMPORTED && |
| 122 | git -C new cat-file commit "$IMPORTED" >actual && |
| 123 | test_grep -E "^gpgsig(-sha256)? " actual && |
| 124 | test_must_be_empty log |
| 125 | ' |
| 126 | |
| 127 | test_expect_success GPG "handle signature invalidated by message change with --signed-commits=$mode" ' |
| 128 | rm -rf new && |
| 129 | git init new && |
| 130 | |
| 131 | git fast-export --signed-commits=verbatim openpgp-signing >output && |
| 132 | |
| 133 | # Change the commit message, which invalidates the signature. |
| 134 | # The commit message length should not change though, otherwise the |
| 135 | # corresponding `data <length>` command would have to be changed too. |
| 136 | sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified && |
| 137 | |
| 138 | if test "$mode" = abort-if-invalid |
| 139 | then |
| 140 | test_must_fail git -C new fast-import --quiet \ |
| 141 | --signed-commits=$mode <modified >log 2>&1 && |
| 142 | test_grep "aborting due to invalid signature" log && |
| 143 | return 0 |
| 144 | fi && |
| 145 | |
| 146 | git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 && |
| 147 | |
| 148 | IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) && |
| 149 | test $OPENPGP_SIGNING != $IMPORTED && |
| 150 | git -C new cat-file commit "$IMPORTED" >actual && |
| 151 | |
| 152 | if test "$mode" = strip-if-invalid |
| 153 | then |
| 154 | test_grep "stripping invalid signature" log && |
| 155 | test_grep ! -E "^gpgsig" actual |
| 156 | else |
| 157 | test_grep "replacing invalid signature" log && |
| 158 | test_grep -E "^gpgsig(-sha256)? " actual && |
| 159 | git -C new verify-commit "$IMPORTED" |
| 160 | fi |
| 161 | ' |
| 162 | |
| 163 | test_expect_success GPGSM "keep valid X.509 signature with --signed-commits=$mode" ' |
| 164 | rm -rf new && |
| 165 | git init new && |
| 166 | |
| 167 | git fast-export --signed-commits=verbatim x509-signing >output && |
| 168 | git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && |
| 169 | IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) && |
| 170 | test $X509_SIGNING = $IMPORTED && |
| 171 | git -C new cat-file commit "$IMPORTED" >actual && |
| 172 | test_grep -E "^gpgsig(-sha256)? " actual && |
| 173 | test_must_be_empty log |
| 174 | ' |
| 175 | |
| 176 | test_expect_success GPGSSH "keep valid SSH signature with --signed-commits=$mode" ' |
| 177 | rm -rf new && |
| 178 | git init new && |
| 179 | |
| 180 | test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && |
| 181 | |
| 182 | git fast-export --signed-commits=verbatim ssh-signing >output && |
| 183 | git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 && |
| 184 | IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) && |
| 185 | test $SSH_SIGNING = $IMPORTED && |
| 186 | git -C new cat-file commit "$IMPORTED" >actual && |
| 187 | test_grep -E "^gpgsig(-sha256)? " actual && |
| 188 | test_must_be_empty log |
| 189 | ' |
| 190 | done |
| 191 | |
| 192 | test_expect_success GPGSSH "sign invalid commit with explicit keyid" ' |
| 193 | rm -rf new && |
| 194 | git init new && |
| 195 | |
| 196 | git fast-export --signed-commits=verbatim ssh-signing >output && |
| 197 | |
| 198 | # Change the commit message, which invalidates the signature. |
| 199 | # The commit message length should not change though, otherwise the |
| 200 | # corresponding `data <length>` command would have to be changed too. |
| 201 | sed "s/SSH signed commit/SSH forged commit/" output >modified && |
| 202 | |
| 203 | # Configure the target repository with an invalid default signing key. |
| 204 | test_config -C new user.signingkey "not-a-real-key-id" && |
| 205 | test_config -C new gpg.format ssh && |
| 206 | test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" && |
| 207 | test_must_fail git -C new fast-import --quiet \ |
| 208 | --signed-commits=sign-if-invalid <modified >/dev/null 2>&1 && |
| 209 | |
| 210 | # Import using explicitly provided signing key. |
| 211 | git -C new fast-import --quiet \ |
| 212 | --signed-commits=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" <modified && |
| 213 | |
| 214 | IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) && |
| 215 | test $SSH_SIGNING != $IMPORTED && |
| 216 | git -C new cat-file commit "$IMPORTED" >actual && |
| 217 | test_grep -E "^gpgsig(-sha256)? " actual && |
| 218 | git -C new verify-commit "$IMPORTED" |
| 219 | ' |
| 220 | |
| 221 | test_done |