Skip to content

Commit 6cd84ff

Browse files
committed
builtin/fast-import: add mode to resign invalid signatures
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 resign the commit as opposed to just stripping the signature. To faciliate this, introduce a "resign-if-invalid" mode for the `--signed-commits` option. Note that commits are resigned 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>
1 parent d213bfa commit 6cd84ff

6 files changed

Lines changed: 120 additions & 72 deletions

File tree

Documentation/git-fast-import.adoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ already trusted to run their own code.
8686
* `strip-if-invalid` will check signatures and, if they are invalid,
8787
will strip them and display a warning. The validation is performed
8888
in the same way as linkgit:git-verify-commit[1] does it.
89+
* `resign-if-invalid` is the same as `strip-if-invalid`, but additionally
90+
resigns the commits with invalid signatures.
8991

9092
Options for Frontends
9193
~~~~~~~~~~~~~~~~~~~~~

builtin/fast-export.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -825,6 +825,9 @@ static void handle_commit(struct commit *commit, struct rev_info *rev,
825825
case SIGN_STRIP_IF_INVALID:
826826
die(_("'strip-if-invalid' is not a valid mode for "
827827
"git fast-export with --signed-commits=<mode>"));
828+
case SIGN_RESIGN_IF_INVALID:
829+
die(_("'resign-if-invalid' is not a valid mode for "
830+
"git fast-export with --signed-commits=<mode>"));
828831
default:
829832
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
830833
}
@@ -970,6 +973,9 @@ static void handle_tag(const char *name, struct tag *tag)
970973
case SIGN_STRIP_IF_INVALID:
971974
die(_("'strip-if-invalid' is not a valid mode for "
972975
"git fast-export with --signed-tags=<mode>"));
976+
case SIGN_RESIGN_IF_INVALID:
977+
die(_("'resign-if-invalid' is not a valid mode for "
978+
"git fast-export with --signed-tags=<mode>"));
973979
default:
974980
BUG("invalid signed_commit_mode value %d", signed_commit_mode);
975981
}

builtin/fast-import.c

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2836,10 +2836,11 @@ static void finalize_commit_buffer(struct strbuf *new_data,
28362836
strbuf_addbuf(new_data, msg);
28372837
}
28382838

2839-
static void handle_strip_if_invalid(struct strbuf *new_data,
2840-
struct signature_data *sig_sha1,
2841-
struct signature_data *sig_sha256,
2842-
struct strbuf *msg)
2839+
static void handle_invalid_signature(struct strbuf *new_data,
2840+
struct signature_data *sig_sha1,
2841+
struct signature_data *sig_sha256,
2842+
struct strbuf *msg,
2843+
int resign)
28432844
{
28442845
struct strbuf tmp_buf = STRBUF_INIT;
28452846
struct signature_check signature_check = { 0 };
@@ -2866,6 +2867,26 @@ static void handle_strip_if_invalid(struct strbuf *new_data,
28662867
warning(_("stripping invalid signature for commit\n"
28672868
" allegedly by %s"), signer);
28682869

2870+
if (resign) {
2871+
struct strbuf signature = STRBUF_INIT;
2872+
struct strbuf payload = STRBUF_INIT;
2873+
char *key = get_signing_key();
2874+
2875+
/*
2876+
* Commits are resigned using only the current object
2877+
* format hash algorithm. Consequently, if multiple
2878+
* signatures are present, the other signature is
2879+
* stripped.
2880+
*/
2881+
strbuf_addstr(&payload, signature_check.payload);
2882+
sign_buffer(&payload, &signature, key);
2883+
add_header_signature(new_data, &signature, the_hash_algo);
2884+
2885+
strbuf_release(&signature);
2886+
strbuf_release(&payload);
2887+
free(key);
2888+
}
2889+
28692890
finalize_commit_buffer(new_data, NULL, NULL, msg);
28702891
} else {
28712892
strbuf_swap(new_data, &tmp_buf);
@@ -2927,6 +2948,7 @@ static void parse_new_commit(const char *arg)
29272948
/* fallthru */
29282949
case SIGN_VERBATIM:
29292950
case SIGN_STRIP_IF_INVALID:
2951+
case SIGN_RESIGN_IF_INVALID:
29302952
import_one_signature(&sig_sha1, &sig_sha256, v);
29312953
break;
29322954

@@ -3011,9 +3033,11 @@ static void parse_new_commit(const char *arg)
30113033
"encoding %s\n",
30123034
encoding);
30133035

3014-
if (signed_commit_mode == SIGN_STRIP_IF_INVALID &&
3036+
if ((signed_commit_mode == SIGN_STRIP_IF_INVALID ||
3037+
signed_commit_mode == SIGN_RESIGN_IF_INVALID) &&
30153038
(sig_sha1.hash_algo || sig_sha256.hash_algo))
3016-
handle_strip_if_invalid(&new_data, &sig_sha1, &sig_sha256, &msg);
3039+
handle_invalid_signature(&new_data, &sig_sha1, &sig_sha256, &msg,
3040+
signed_commit_mode == SIGN_RESIGN_IF_INVALID);
30173041
else
30183042
finalize_commit_buffer(&new_data, &sig_sha1, &sig_sha256, &msg);
30193043

@@ -3060,6 +3084,9 @@ static void handle_tag_signature(struct strbuf *msg, const char *name)
30603084
case SIGN_STRIP_IF_INVALID:
30613085
die(_("'strip-if-invalid' is not a valid mode for "
30623086
"git fast-import with --signed-tags=<mode>"));
3087+
case SIGN_RESIGN_IF_INVALID:
3088+
die(_("'resign-if-invalid' is not a valid mode for "
3089+
"git fast-import with --signed-tags=<mode>"));
30633090
default:
30643091
BUG("invalid signed_tag_mode value %d from tag '%s'",
30653092
signed_tag_mode, name);

gpg-interface.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,8 @@ int parse_sign_mode(const char *arg, enum sign_mode *mode)
11561156
*mode = SIGN_STRIP;
11571157
else if (!strcmp(arg, "strip-if-invalid"))
11581158
*mode = SIGN_STRIP_IF_INVALID;
1159+
else if (!strcmp(arg, "resign-if-invalid"))
1160+
*mode = SIGN_RESIGN_IF_INVALID;
11591161
else
11601162
return -1;
11611163
return 0;

gpg-interface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ enum sign_mode {
112112
SIGN_WARN_STRIP,
113113
SIGN_STRIP,
114114
SIGN_STRIP_IF_INVALID,
115+
SIGN_RESIGN_IF_INVALID,
115116
};
116117

117118
/*

t/t9305-fast-import-signatures.sh

Lines changed: 76 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -103,71 +103,81 @@ test_expect_success GPG 'strip both OpenPGP signatures with --signed-commits=war
103103
test_line_count = 2 out
104104
'
105105

106-
test_expect_success GPG 'import commit with no signature with --signed-commits=strip-if-invalid' '
107-
git fast-export main >output &&
108-
git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
109-
test_must_be_empty log
110-
'
111-
112-
test_expect_success GPG 'keep valid OpenPGP signature with --signed-commits=strip-if-invalid' '
113-
rm -rf new &&
114-
git init new &&
115-
116-
git fast-export --signed-commits=verbatim openpgp-signing >output &&
117-
git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
118-
IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
119-
test $OPENPGP_SIGNING = $IMPORTED &&
120-
git -C new cat-file commit "$IMPORTED" >actual &&
121-
test_grep -E "^gpgsig(-sha256)? " actual &&
122-
test_must_be_empty log
123-
'
124-
125-
test_expect_success GPG 'strip signature invalidated by message change with --signed-commits=strip-if-invalid' '
126-
rm -rf new &&
127-
git init new &&
128-
129-
git fast-export --signed-commits=verbatim openpgp-signing >output &&
130-
131-
# Change the commit message, which invalidates the signature.
132-
# The commit message length should not change though, otherwise the
133-
# corresponding `data <length>` command would have to be changed too.
134-
sed "s/OpenPGP signed commit/OpenPGP forged commit/" output >modified &&
135-
136-
git -C new fast-import --quiet --signed-commits=strip-if-invalid <modified >log 2>&1 &&
137-
138-
IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
139-
test $OPENPGP_SIGNING != $IMPORTED &&
140-
git -C new cat-file commit "$IMPORTED" >actual &&
141-
test_grep ! -E "^gpgsig" actual &&
142-
test_grep "stripping invalid signature" log
143-
'
144-
145-
test_expect_success GPGSM 'keep valid X.509 signature with --signed-commits=strip-if-invalid' '
146-
rm -rf new &&
147-
git init new &&
148-
149-
git fast-export --signed-commits=verbatim x509-signing >output &&
150-
git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
151-
IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
152-
test $X509_SIGNING = $IMPORTED &&
153-
git -C new cat-file commit "$IMPORTED" >actual &&
154-
test_grep -E "^gpgsig(-sha256)? " actual &&
155-
test_must_be_empty log
156-
'
157-
158-
test_expect_success GPGSSH 'keep valid SSH signature with --signed-commits=strip-if-invalid' '
159-
rm -rf new &&
160-
git init new &&
161-
162-
test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
163-
164-
git fast-export --signed-commits=verbatim ssh-signing >output &&
165-
git -C new fast-import --quiet --signed-commits=strip-if-invalid <output >log 2>&1 &&
166-
IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
167-
test $SSH_SIGNING = $IMPORTED &&
168-
git -C new cat-file commit "$IMPORTED" >actual &&
169-
test_grep -E "^gpgsig(-sha256)? " actual &&
170-
test_must_be_empty log
171-
'
106+
for mode in strip-if-invalid resign-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 "strip 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+
git -C new fast-import --quiet --signed-commits=$mode <modified >log 2>&1 &&
139+
140+
IMPORTED=$(git -C new rev-parse --verify refs/heads/openpgp-signing) &&
141+
test $OPENPGP_SIGNING != $IMPORTED &&
142+
git -C new cat-file commit "$IMPORTED" >actual &&
143+
test_grep "stripping invalid signature" log &&
144+
145+
if test "$mode" = strip-if-invalid
146+
then
147+
test_grep ! -E "^gpgsig" actual
148+
else
149+
test_grep -E "^gpgsig(-sha256)? " actual &&
150+
git -C new verify-commit "$IMPORTED"
151+
fi
152+
'
153+
154+
test_expect_success GPGSM "keep valid X.509 signature with --signed-commits=$mode" '
155+
rm -rf new &&
156+
git init new &&
157+
158+
git fast-export --signed-commits=verbatim x509-signing >output &&
159+
git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
160+
IMPORTED=$(git -C new rev-parse --verify refs/heads/x509-signing) &&
161+
test $X509_SIGNING = $IMPORTED &&
162+
git -C new cat-file commit "$IMPORTED" >actual &&
163+
test_grep -E "^gpgsig(-sha256)? " actual &&
164+
test_must_be_empty log
165+
'
166+
167+
test_expect_success GPGSSH "keep valid SSH signature with --signed-commits=$mode" '
168+
rm -rf new &&
169+
git init new &&
170+
171+
test_config -C new gpg.ssh.allowedSignersFile "${GPGSSH_ALLOWED_SIGNERS}" &&
172+
173+
git fast-export --signed-commits=verbatim ssh-signing >output &&
174+
git -C new fast-import --quiet --signed-commits=$mode <output >log 2>&1 &&
175+
IMPORTED=$(git -C new rev-parse --verify refs/heads/ssh-signing) &&
176+
test $SSH_SIGNING = $IMPORTED &&
177+
git -C new cat-file commit "$IMPORTED" >actual &&
178+
test_grep -E "^gpgsig(-sha256)? " actual &&
179+
test_must_be_empty log
180+
'
181+
done
172182

173183
test_done

0 commit comments

Comments
 (0)