]> git.ipfire.org Git - thirdparty/git.git/commitdiff
fast-import: add 'sign-if-invalid' mode to '--signed-tags=<mode>'
authorJustin Tobler <jltobler@gmail.com>
Thu, 26 Mar 2026 19:14:13 +0000 (14:14 -0500)
committerJunio C Hamano <gitster@pobox.com>
Thu, 26 Mar 2026 19:42:58 +0000 (12:42 -0700)
With ee66c793f8 (fast-import: add mode to sign commits with invalid
signatures, 2026-03-12), git-fast-import(1) learned to verify commit
signatures during import and replace signatures that fail verification
with a newly generated one. Extend the same behavior to signed tag
objects by introducing a 'sign-if-invalid' mode for the '--signed-tags'
option.

Signed-off-by: Justin Tobler <jltobler@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/fast-import.c
t/t9306-fast-import-signed-tags.sh

index 5e89829aea0fe9ca26b856f50b434191cd9123d7..783e0e7ab47e89bd47b003e04554a17839f6c634 100644 (file)
@@ -191,6 +191,7 @@ static const char *global_prefix;
 static enum sign_mode signed_tag_mode = SIGN_VERBATIM;
 static enum sign_mode signed_commit_mode = SIGN_VERBATIM;
 static const char *signed_commit_keyid;
+static const char *signed_tag_keyid;
 
 /* Memory pools */
 static struct mem_pool fi_mem_pool = {
@@ -3110,6 +3111,19 @@ static void handle_tag_signature_if_invalid(struct strbuf *buf,
 
        strbuf_setlen(msg, sig_offset);
 
+       if (signed_tag_mode == SIGN_SIGN_IF_INVALID) {
+               strbuf_attach(&payload, sigc.payload, sigc.payload_len,
+                             sigc.payload_len + 1);
+               sigc.payload = NULL;
+               strbuf_reset(&signature);
+
+               if (sign_buffer(&payload, &signature, signed_tag_keyid,
+                               SIGN_BUFFER_USE_DEFAULT_KEY))
+                       die(_("failed to sign tag object"));
+
+               strbuf_addbuf(msg, &signature);
+       }
+
 out:
        signature_check_clear(&sigc);
        strbuf_release(&signature);
@@ -3142,6 +3156,7 @@ static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const c
                /* Truncate the buffer to remove the signature */
                strbuf_setlen(msg, sig_offset);
                break;
+       case SIGN_SIGN_IF_INVALID:
        case SIGN_STRIP_IF_INVALID:
                handle_tag_signature_if_invalid(buf, msg, sig_offset);
                break;
@@ -3153,9 +3168,6 @@ static void handle_tag_signature(struct strbuf *buf, struct strbuf *msg, const c
        case SIGN_ABORT_IF_INVALID:
                die(_("'abort-if-invalid' is not a valid mode for "
                      "git fast-import with --signed-tags=<mode>"));
-       case SIGN_SIGN_IF_INVALID:
-               die(_("'sign-if-invalid' is not a valid mode for "
-                     "git fast-import with --signed-tags=<mode>"));
        default:
                BUG("invalid signed_tag_mode value %d from tag '%s'",
                    signed_tag_mode, name);
@@ -3749,7 +3761,7 @@ static int parse_one_option(const char *option)
                if (parse_sign_mode(option, &signed_commit_mode, &signed_commit_keyid))
                        usagef(_("unknown --signed-commits mode '%s'"), option);
        } else if (skip_prefix(option, "signed-tags=", &option)) {
-               if (parse_sign_mode(option, &signed_tag_mode, NULL))
+               if (parse_sign_mode(option, &signed_tag_mode, &signed_tag_keyid))
                        usagef(_("unknown --signed-tags mode '%s'"), option);
        } else if (!strcmp(option, "quiet")) {
                show_stats = 0;
index fd43b0b52a29a2c6b1f04b0cf0f9ccc5ddf677e9..bb4c8008efe5b8e962097c23bbd839e96f535266 100755 (executable)
@@ -77,7 +77,7 @@ test_expect_success GPGSSH 'import SSH signed tag with --signed-tags=strip' '
        test_grep ! "SSH SIGNATURE" out
 '
 
-for mode in strip-if-invalid
+for mode in strip-if-invalid sign-if-invalid
 do
        test_expect_success GPG "import tag with no signature with --signed-tags=$mode" '
                test_when_finished rm -rf import &&
@@ -117,7 +117,15 @@ do
                IMPORTED=$(git -C import rev-parse --verify refs/tags/openpgp-signed) &&
                test $OPENPGP_SIGNED != $IMPORTED &&
                git -C import cat-file tag "$IMPORTED" >actual &&
-               test_grep ! -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+
+               if test "$mode" = strip-if-invalid
+               then
+                       test_grep ! -E "^-----BEGIN PGP SIGNATURE-----" actual
+               else
+                       test_grep -E "^-----BEGIN PGP SIGNATURE-----" actual &&
+                       git -C import verify-tag "$IMPORTED"
+               fi &&
+
                test_must_be_empty log
        '
 
@@ -150,4 +158,33 @@ do
        '
 done
 
+test_expect_success GPGSSH 'sign invalid tag with explicit keyid' '
+       test_when_finished rm -rf import &&
+       git init import &&
+
+       git fast-export --signed-tags=verbatim ssh-signed >output &&
+
+       # Change the tag message, which invalidates the signature. The tag
+       # message length should not change though, otherwise the corresponding
+       # `data <length>` command would have to be changed too.
+       sed "s/SSH signed tag/SSH forged tag/" output >modified &&
+
+       # Configure the target repository with an invalid default signing key.
+       test_config -C import user.signingkey "not-a-real-key-id" &&
+       test_config -C import gpg.format ssh &&
+       test_config -C import gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+       test_must_fail git -C import fast-import --quiet \
+               --signed-tags=sign-if-invalid <modified >/dev/null 2>&1 &&
+
+       # Import using explicitly provided signing key.
+       git -C import fast-import --quiet \
+               --signed-tags=sign-if-invalid="${GPGSSH_KEY_PRIMARY}" <modified &&
+
+       IMPORTED=$(git -C import rev-parse --verify refs/tags/ssh-signed) &&
+       test $SSH_SIGNED != $IMPORTED &&
+       git -C import cat-file tag "$IMPORTED" >actual &&
+       test_grep -E "^-----BEGIN SSH SIGNATURE-----" actual &&
+       git -C import verify-tag "$IMPORTED"
+'
+
 test_done