From: Justin Tobler <jltobler@gmail.com>
To: git@vger.kernel.org
Cc: sandals@crustytoothpaste.net, christian.couder@gmail.com,
Justin Tobler <jltobler@gmail.com>
Subject: [PATCH 2/2] fast-import: add mode to re-sign invalid commit signatures
Date: Mon, 23 Feb 2026 13:41:46 -0600 [thread overview]
Message-ID: <20260223194146.3476768-3-jltobler@gmail.com> (raw)
In-Reply-To: <20260223194146.3476768-1-jltobler@gmail.com>
With git-fast-import(1), handling of signed commits is controlled via
the `--signed-commits=<mode>` option. When an invalid signature is
encountered, a user may want the option to re-sign the commit as opposed
to just stripping the signature. To faciliate this, introduce a
"re-sign-if-invalid" mode for the `--signed-commits` option.
Note that commits are re-signed using only the repository object format
hash algorithm. If a commit has an additional signature due to the
`compatObjectFormat` repository extension being set, the other signature
is stripped.
Signed-off-by: Justin Tobler <jltobler@gmail.com>
---
Documentation/git-fast-import.adoc | 3 +
builtin/fast-export.c | 6 ++
builtin/fast-import.c | 43 +++++++--
gpg-interface.c | 2 +
gpg-interface.h | 1 +
t/t9305-fast-import-signatures.sh | 142 +++++++++++++++--------------
6 files changed, 125 insertions(+), 72 deletions(-)
diff --git a/Documentation/git-fast-import.adoc b/Documentation/git-fast-import.adoc
index 479c4081da..b902a6e2b0 100644
--- a/Documentation/git-fast-import.adoc
+++ b/Documentation/git-fast-import.adoc
@@ -86,6 +86,9 @@ already trusted to run their own code.
* `strip-if-invalid` will check signatures and, if they are invalid,
will strip them and display a warning. The validation is performed
in the same way as linkgit:git-verify-commit[1] does it.
+* `re-sign-if-invalid` is the same as `strip-if-invalid`, but additionally the
+ commits with invalid signatures are signed again, so that old invalid
+ signatures are replaced with new valid ones.
Options for Frontends
~~~~~~~~~~~~~~~~~~~~~
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 0c5d2386d8..76fad1dec5 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -825,6 +825,9 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
case SIGN_STRIP_IF_INVALID:
die(_("'strip-if-invalid' is not a valid mode for "
"git fast-export with --signed-commits=<mode>"));
+ case SIGN_RESIGN_IF_INVALID:
+ die(_("'re-sign-if-invalid' is not a valid mode for "
+ "git fast-export with --signed-commits=<mode>"));
default:
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
}
@@ -970,6 +973,9 @@ static void handle_tag(const char *name, struct tag *tag)
case SIGN_STRIP_IF_INVALID:
die(_("'strip-if-invalid' is not a valid mode for "
"git fast-export with --signed-tags=<mode>"));
+ case SIGN_RESIGN_IF_INVALID:
+ die(_("'re-sign-if-invalid' is not a valid mode for "
+ "git fast-export with --signed-tags=<mode>"));
default:
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
}
diff --git a/builtin/fast-import.c b/builtin/fast-import.c
index b8a7757cfd..e34a373d2f 100644
--- a/builtin/fast-import.c
+++ b/builtin/fast-import.c
@@ -2836,10 +2836,11 @@ static void finalize_commit_buffer(struct strbuf *new_data,
strbuf_addbuf(new_data, msg);
}
-static void handle_strip_if_invalid(struct strbuf *new_data,
- struct signature_data *sig_sha1,
- struct signature_data *sig_sha256,
- struct strbuf *msg)
+static void handle_invalid_signature(struct strbuf *new_data,
+ struct signature_data *sig_sha1,
+ struct signature_data *sig_sha256,
+ struct strbuf *msg,
+ enum sign_mode mode)
{
struct strbuf tmp_buf = STRBUF_INIT;
struct signature_check signature_check = { 0 };
@@ -2866,6 +2867,30 @@ static void handle_strip_if_invalid(struct strbuf *new_data,
warning(_("stripping invalid signature for commit\n"
" allegedly by %s"), signer);
+ if (mode == SIGN_RESIGN_IF_INVALID) {
+ struct strbuf signature = STRBUF_INIT;
+ struct strbuf payload = STRBUF_INIT;
+ char *key = get_signing_key();
+
+ /*
+ * Commits are resigned using the repository object
+ * format hash algorithm only. Consequently if
+ * extensions.compatObjectFormat is set, the
+ * compatability hash is not currently used to
+ * additionally sign the commit. If the commit payload
+ * were reconstructed in the compatability format, it
+ * would be possible to generate the other signature
+ * accordingly though.
+ */
+ strbuf_addstr(&payload, signature_check.payload);
+ sign_buffer(&payload, &signature, key);
+ add_header_signature(new_data, &signature, the_hash_algo);
+
+ strbuf_release(&signature);
+ strbuf_release(&payload);
+ free(key);
+ }
+
finalize_commit_buffer(new_data, NULL, NULL, msg);
} else {
strbuf_swap(new_data, &tmp_buf);
@@ -2927,6 +2952,7 @@ static void parse_new_commit(const char *arg)
/* fallthru */
case SIGN_VERBATIM:
case SIGN_STRIP_IF_INVALID:
+ case SIGN_RESIGN_IF_INVALID:
import_one_signature(&sig_sha1, &sig_sha256, v);
break;
@@ -3011,9 +3037,11 @@ static void parse_new_commit(const char *arg)
"encoding %s\n",
encoding);
- if (signed_commit_mode == SIGN_STRIP_IF_INVALID &&
+ if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
+ signed_commit_mode == SIGN_RESIGN_IF_INVALID) &&
(sig_sha1.hash_algo || sig_sha256.hash_algo))
- handle_strip_if_invalid(&new_data, &sig_sha1, &sig_sha256, &msg);
+ handle_invalid_signature(&new_data, &sig_sha1, &sig_sha256, &msg,
+ signed_commit_mode);
else
finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg);
@@ -3060,6 +3088,9 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
case SIGN_STRIP_IF_INVALID:
die(_("'strip-if-invalid' is not a valid mode for "
"git fast-import with --signed-tags=<mode>"));
+ case SIGN_RESIGN_IF_INVALID:
+ die(_("'re-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);
diff --git a/gpg-interface.c b/gpg-interface.c
index 87fb6605fb..e7eb42d9d6 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -1156,6 +1156,8 @@ int parse_sign_mode(const char *arg, enum sign_mode *mode)
*mode = SIGN_STRIP;
else if (!strcmp(arg, "strip-if-invalid"))
*mode = SIGN_STRIP_IF_INVALID;
+ else if (!strcmp(arg, "re-sign-if-invalid"))
+ *mode = SIGN_RESIGN_IF_INVALID;
else
return -1;
return 0;
diff --git a/gpg-interface.h b/gpg-interface.h
index 789d1ffac4..2ab2a21e1a 100644
--- a/gpg-interface.h
+++ b/gpg-interface.h
@@ -112,6 +112,7 @@ enum sign_mode {
SIGN_WARN_STRIP,
SIGN_STRIP,
SIGN_STRIP_IF_INVALID,
+ SIGN_RESIGN_IF_INVALID,
};
/*
diff --git a/t/t9305-fast-import-signatures.sh b/t/t9305-fast-import-signatures.sh
index 022dae02e4..b52fb75976 100755
--- a/t/t9305-fast-import-signatures.sh
+++ b/t/t9305-fast-import-signatures.sh
@@ -103,71 +103,81 @@ test_expect_success GPG 'strip both OpenPGP signatures with --signed-commits=war
test_line_count = 2 out
'
-test_expect_success GPG 'import commit with no signature with --signed-commits=strip-if-invalid' '
- git fast-export main >output &&
- git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
- test_must_be_empty log
-'
-
-test_expect_success GPG 'keep valid OpenPGP signature with --signed-commits=strip-if-invalid' '
- rm -rf new &&
- git init new &&
-
- git fast-export --signed-commits=verbatim openpgp-signing >output &&
- git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
- IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
- test $OPENPGP_SIGNING = $IMPORTED &&
- git -C new cat-file commit "$IMPORTED" >actual &&
- test_grep -E "^gpgsig(-sha256)? " actual &&
- test_must_be_empty log
-'
-
-test_expect_success GPG 'strip signature invalidated by message change with --signed-commits=strip-if-invalid' '
- rm -rf new &&
- git init new &&
-
- git fast-export --signed-commits=verbatim openpgp-signing >output &&
-
- # Change the commit message, which invalidates the signature.
- # The commit message length should not change though, otherwise the
- # corresponding `data <length>` command would have to be changed too.
- sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified &&
-
- git -C new fast-import --quiet --signed-commits=strip-if-invalid <modified >log 2>&1 &&
-
- IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
- test $OPENPGP_SIGNING != $IMPORTED &&
- git -C new cat-file commit "$IMPORTED" >actual &&
- test_grep ! -E "^gpgsig" actual &&
- test_grep "stripping invalid signature" log
-'
-
-test_expect_success GPGSM 'keep valid X.509 signature with --signed-commits=strip-if-invalid' '
- rm -rf new &&
- git init new &&
-
- git fast-export --signed-commits=verbatim x509-signing >output &&
- git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
- IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
- test $X509_SIGNING = $IMPORTED &&
- git -C new cat-file commit "$IMPORTED" >actual &&
- test_grep -E "^gpgsig(-sha256)? " actual &&
- test_must_be_empty log
-'
-
-test_expect_success GPGSSH 'keep valid SSH signature with --signed-commits=strip-if-invalid' '
- rm -rf new &&
- git init new &&
-
- test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
-
- git fast-export --signed-commits=verbatim ssh-signing >output &&
- git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
- IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
- test $SSH_SIGNING = $IMPORTED &&
- git -C new cat-file commit "$IMPORTED" >actual &&
- test_grep -E "^gpgsig(-sha256)? " actual &&
- test_must_be_empty log
-'
+for mode in strip-if-invalid re-sign-if-invalid
+do
+ test_expect_success GPG "import commit with no signature with --signed-commits=$mode" '
+ git fast-export main >output &&
+ git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
+ test_must_be_empty log
+ '
+
+ test_expect_success GPG "keep valid OpenPGP signature with --signed-commits=$mode" '
+ rm -rf new &&
+ git init new &&
+
+ git fast-export --signed-commits=verbatim openpgp-signing >output &&
+ git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
+ test $OPENPGP_SIGNING = $IMPORTED &&
+ git -C new cat-file commit "$IMPORTED" >actual &&
+ test_grep -E "^gpgsig(-sha256)? " actual &&
+ test_must_be_empty log
+ '
+
+ test_expect_success GPG "strip signature invalidated by message change with --signed-commits=$mode" '
+ rm -rf new &&
+ git init new &&
+
+ git fast-export --signed-commits=verbatim openpgp-signing >output &&
+
+ # Change the commit message, which invalidates the signature.
+ # The commit message length should not change though, otherwise the
+ # corresponding `data <length>` command would have to be changed too.
+ sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified &&
+
+ git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 &&
+
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
+ test $OPENPGP_SIGNING != $IMPORTED &&
+ git -C new cat-file commit "$IMPORTED" >actual &&
+ test_grep "stripping invalid signature" log &&
+
+ if test "$mode" = strip-if-invalid
+ then
+ test_grep ! -E "^gpgsig" actual
+ else
+ test_grep -E "^gpgsig(-sha256)? " actual &&
+ git -C new verify-commit "$IMPORTED"
+ fi
+ '
+
+ test_expect_success GPGSM "keep valid X.509 signature with --signed-commits=$mode" '
+ rm -rf new &&
+ git init new &&
+
+ git fast-export --signed-commits=verbatim x509-signing >output &&
+ git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
+ test $X509_SIGNING = $IMPORTED &&
+ git -C new cat-file commit "$IMPORTED" >actual &&
+ test_grep -E "^gpgsig(-sha256)? " actual &&
+ test_must_be_empty log
+ '
+
+ test_expect_success GPGSSH "keep valid SSH signature with --signed-commits=$mode" '
+ rm -rf new &&
+ git init new &&
+
+ test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
+
+ git fast-export --signed-commits=verbatim ssh-signing >output &&
+ git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
+ IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
+ test $SSH_SIGNING = $IMPORTED &&
+ git -C new cat-file commit "$IMPORTED" >actual &&
+ test_grep -E "^gpgsig(-sha256)? " actual &&
+ test_must_be_empty log
+ '
+done
test_done
--
2.53.0
next prev parent reply other threads:[~2026-02-23 19:42 UTC|newest]
Thread overview: 60+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-02-23 19:41 [PATCH 0/2] fast-import: add mode to re-sign invalid commit signatures Justin Tobler
2026-02-23 19:41 ` [PATCH 1/2] commit: remove unused forward declaration Justin Tobler
2026-02-24 9:35 ` Patrick Steinhardt
2026-02-23 19:41 ` Justin Tobler [this message]
2026-02-24 9:33 ` [PATCH 2/2] fast-import: add mode to re-sign invalid commit signatures Patrick Steinhardt
2026-02-24 18:33 ` Justin Tobler
2026-02-24 13:40 ` [PATCH 0/2] " Christian Couder
2026-02-24 22:41 ` brian m. carlson
2026-02-24 22:45 ` Junio C Hamano
2026-03-02 22:49 ` Justin Tobler
2026-03-06 20:53 ` [PATCH v2 0/3] " Justin Tobler
2026-03-06 20:53 ` [PATCH v2 1/3] commit: remove unused forward declaration Justin Tobler
2026-03-06 20:53 ` [PATCH v2 2/3] gpg-interface: introduce sign_buffer_with_key() Justin Tobler
2026-03-10 9:01 ` Christian Couder
2026-03-10 18:04 ` Justin Tobler
2026-03-06 20:53 ` [PATCH v2 3/3] fast-import: add mode to re-sign invalid commit signatures Justin Tobler
2026-03-10 9:27 ` Christian Couder
2026-03-10 18:09 ` Justin Tobler
2026-03-10 20:11 ` [PATCH v3 0/3] " Justin Tobler
2026-03-10 20:11 ` [PATCH v3 1/3] commit: remove unused forward declaration Justin Tobler
2026-03-10 22:29 ` Junio C Hamano
2026-03-10 20:11 ` [PATCH v3 2/3] gpg-interface: introduce sign_buffer_with_key() Justin Tobler
2026-03-10 22:33 ` Junio C Hamano
2026-03-10 20:11 ` [PATCH v3 3/3] fast-import: add mode to re-sign invalid commit signatures Justin Tobler
2026-03-10 20:49 ` [PATCH v3 0/3] " Junio C Hamano
2026-03-10 21:06 ` Justin Tobler
2026-03-10 21:20 ` Junio C Hamano
2026-03-10 22:13 ` Justin Tobler
2026-03-10 22:39 ` Junio C Hamano
2026-03-10 23:03 ` Justin Tobler
2026-03-11 17:31 ` [PATCH v4 " Justin Tobler
2026-03-11 17:31 ` [PATCH v4 1/3] commit: remove unused forward declaration Justin Tobler
2026-03-11 17:31 ` [PATCH v4 2/3] gpg-interface: introduce sign_buffer_with_key() Justin Tobler
2026-03-12 10:22 ` Patrick Steinhardt
2026-03-12 13:58 ` Justin Tobler
2026-03-11 17:31 ` [PATCH v4 3/3] fast-import: add mode to sign commits with invalid signatures Justin Tobler
2026-03-12 10:23 ` Patrick Steinhardt
2026-03-12 14:08 ` Justin Tobler
2026-03-12 14:22 ` Patrick Steinhardt
2026-03-12 17:21 ` Justin Tobler
2026-03-12 19:22 ` [PATCH v5 0/3] fast-import: add mode to re-sign invalid commit signatures Justin Tobler
2026-03-12 19:22 ` [PATCH v5 1/3] commit: remove unused forward declaration Justin Tobler
2026-03-12 19:22 ` [PATCH v5 2/3] gpg-interface: allow sign_buffer() to use default signing key Justin Tobler
2026-03-12 20:20 ` Junio C Hamano
2026-03-12 20:24 ` Justin Tobler
2026-03-12 19:22 ` [PATCH v5 3/3] fast-import: add mode to sign commits with invalid signatures Justin Tobler
2026-03-12 20:20 ` Junio C Hamano
2026-03-12 20:29 ` Justin Tobler
2026-03-12 23:58 ` Jeff King
2026-03-13 0:17 ` Justin Tobler
2026-03-12 20:20 ` [PATCH v5 0/3] fast-import: add mode to re-sign invalid commit signatures Junio C Hamano
2026-03-12 20:30 ` Justin Tobler
2026-03-13 1:39 ` [PATCH v6 " Justin Tobler
2026-03-13 1:39 ` [PATCH v6 1/3] commit: remove unused forward declaration Justin Tobler
2026-03-13 1:39 ` [PATCH v6 2/3] gpg-interface: allow sign_buffer() to use default signing key Justin Tobler
2026-03-13 6:31 ` Patrick Steinhardt
2026-03-13 1:39 ` [PATCH v6 3/3] fast-import: add mode to sign commits with invalid signatures Justin Tobler
2026-03-13 6:31 ` Patrick Steinhardt
2026-03-13 4:29 ` [PATCH v6 0/3] fast-import: add mode to re-sign invalid commit signatures Junio C Hamano
2026-03-13 6:31 ` Patrick Steinhardt
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260223194146.3476768-3-jltobler@gmail.com \
--to=jltobler@gmail.com \
--cc=christian.couder@gmail.com \
--cc=git@vger.kernel.org \
--cc=sandals@crustytoothpaste.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox