summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config.txt10
-rw-r--r--gpg-interface.c108
-rwxr-xr-xt/lib-gpg.sh28
-rw-r--r--t/lib-gpg/gpgsm-gen-key.in8
-rw-r--r--t/lib-gpg/gpgsm_cert.p12bin0 -> 2652 bytes
-rwxr-xr-xt/t4202-log.sh37
-rwxr-xr-xt/t5534-push-signed.sh63
-rwxr-xr-xt/t7004-tag.sh13
-rwxr-xr-xt/t7030-verify-tag.sh34
-rwxr-xr-xt/t7510-signed-commit.sh7
10 files changed, 285 insertions, 23 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 3a4ff57..fd8d27e 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -1884,6 +1884,16 @@ gpg.program::
signed, and the program is expected to send the result to its
standard output.
+gpg.format::
+ Specifies which key format to use when signing with `--gpg-sign`.
+ Default is "openpgp" and another possible value is "x509".
+
+gpg.<format>.program::
+ Use this to customize the program used for the signing format you
+ chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
+ be used as a legacy synonym for `gpg.openpgp.program`. The default
+ value for `gpg.x509.program` is "gpgsm".
+
gui.commitMsgWidth::
Defines how wide the commit message window is in the
linkgit:git-gui[1]. "75" is the default.
diff --git a/gpg-interface.c b/gpg-interface.c
index 09ddfbc..bb8ea66 100644
--- a/gpg-interface.c
+++ b/gpg-interface.c
@@ -7,10 +7,64 @@
#include "tempfile.h"
static char *configured_signing_key;
-static const char *gpg_program = "gpg";
+struct gpg_format {
+ const char *name;
+ const char *program;
+ const char **verify_args;
+ const char **sigs;
+};
+
+static const char *openpgp_verify_args[] = {
+ "--keyid-format=long",
+ NULL
+};
+static const char *openpgp_sigs[] = {
+ "-----BEGIN PGP SIGNATURE-----",
+ "-----BEGIN PGP MESSAGE-----",
+ NULL
+};
+
+static const char *x509_verify_args[] = {
+ NULL
+};
+static const char *x509_sigs[] = {
+ "-----BEGIN SIGNED MESSAGE-----",
+ NULL
+};
-#define PGP_SIGNATURE "-----BEGIN PGP SIGNATURE-----"
-#define PGP_MESSAGE "-----BEGIN PGP MESSAGE-----"
+static struct gpg_format gpg_format[] = {
+ { .name = "openpgp", .program = "gpg",
+ .verify_args = openpgp_verify_args,
+ .sigs = openpgp_sigs
+ },
+ { .name = "x509", .program = "gpgsm",
+ .verify_args = x509_verify_args,
+ .sigs = x509_sigs
+ },
+};
+
+static struct gpg_format *use_format = &gpg_format[0];
+
+static struct gpg_format *get_format_by_name(const char *str)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(gpg_format); i++)
+ if (!strcmp(gpg_format[i].name, str))
+ return gpg_format + i;
+ return NULL;
+}
+
+static struct gpg_format *get_format_by_sig(const char *sig)
+{
+ int i, j;
+
+ for (i = 0; i < ARRAY_SIZE(gpg_format); i++)
+ for (j = 0; gpg_format[i].sigs[j]; j++)
+ if (starts_with(sig, gpg_format[i].sigs[j]))
+ return gpg_format + i;
+ return NULL;
+}
void signature_check_clear(struct signature_check *sigc)
{
@@ -53,10 +107,11 @@ static void parse_gpg_output(struct signature_check *sigc)
sigc->result = sigcheck_gpg_status[i].result;
/* The trust messages are not followed by key/signer information */
if (sigc->result != 'U') {
- sigc->key = xmemdupz(found, 16);
+ next = strchrnul(found, ' ');
+ sigc->key = xmemdupz(found, next - found);
/* The ERRSIG message is not followed by signer information */
- if (sigc-> result != 'E') {
- found += 17;
+ if (*next && sigc-> result != 'E') {
+ found = next + 1;
next = strchrnul(found, '\n');
sigc->signer = xmemdupz(found, next - found);
}
@@ -101,12 +156,6 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
fputs(output, stderr);
}
-static int is_gpg_start(const char *line)
-{
- return starts_with(line, PGP_SIGNATURE) ||
- starts_with(line, PGP_MESSAGE);
-}
-
size_t parse_signature(const char *buf, size_t size)
{
size_t len = 0;
@@ -114,7 +163,7 @@ size_t parse_signature(const char *buf, size_t size)
while (len < size) {
const char *eol;
- if (is_gpg_start(buf + len))
+ if (get_format_by_sig(buf + len))
match = len;
eol = memchr(buf + len, '\n', size - len);
@@ -131,6 +180,9 @@ void set_signing_key(const char *key)
int git_gpg_config(const char *var, const char *value, void *cb)
{
+ struct gpg_format *fmt = NULL;
+ char *fmtname = NULL;
+
if (!strcmp(var, "user.signingkey")) {
if (!value)
return config_error_nonbool(var);
@@ -138,13 +190,28 @@ int git_gpg_config(const char *var, const char *value, void *cb)
return 0;
}
- if (!strcmp(var, "gpg.program")) {
+ if (!strcmp(var, "gpg.format")) {
if (!value)
return config_error_nonbool(var);
- gpg_program = xstrdup(value);
+ fmt = get_format_by_name(value);
+ if (!fmt)
+ return error("unsupported value for %s: %s",
+ var, value);
+ use_format = fmt;
return 0;
}
+ if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
+ fmtname = "openpgp";
+
+ if (!strcmp(var, "gpg.x509.program"))
+ fmtname = "x509";
+
+ if (fmtname) {
+ fmt = get_format_by_name(fmtname);
+ return git_config_string(&fmt->program, var, value);
+ }
+
return 0;
}
@@ -163,7 +230,7 @@ int sign_buffer(struct strbuf *buffer, struct strbuf *signature, const char *sig
struct strbuf gpg_status = STRBUF_INIT;
argv_array_pushl(&gpg.args,
- gpg_program,
+ use_format->program,
"--status-fd=2",
"-bsau", signing_key,
NULL);
@@ -201,6 +268,7 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
struct strbuf *gpg_output, struct strbuf *gpg_status)
{
struct child_process gpg = CHILD_PROCESS_INIT;
+ struct gpg_format *fmt;
struct tempfile *temp;
int ret;
struct strbuf buf = STRBUF_INIT;
@@ -216,10 +284,14 @@ int verify_signed_buffer(const char *payload, size_t payload_size,
return -1;
}
+ fmt = get_format_by_sig(signature);
+ if (!fmt)
+ BUG("bad signature '%s'", signature);
+
+ argv_array_push(&gpg.args, fmt->program);
+ argv_array_pushv(&gpg.args, fmt->verify_args);
argv_array_pushl(&gpg.args,
- gpg_program,
"--status-fd=1",
- "--keyid-format=long",
"--verify", temp->filename.buf, "-",
NULL);
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index a5d3b2c..3fe0287 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -38,7 +38,33 @@ then
"$TEST_DIRECTORY"/lib-gpg/ownertrust &&
gpg --homedir "${GNUPGHOME}" </dev/null >/dev/null 2>&1 \
--sign -u committer@example.com &&
- test_set_prereq GPG
+ test_set_prereq GPG &&
+ # Available key info:
+ # * see t/lib-gpg/gpgsm-gen-key.in
+ # To generate new certificate:
+ # * no passphrase
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o /tmp/gpgsm.crt.user \
+ # --generate-key \
+ # --batch t/lib-gpg/gpgsm-gen-key.in
+ # To import certificate:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # --import /tmp/gpgsm.crt.user
+ # To export into a .p12 we can later import:
+ # gpgsm --homedir /tmp/gpghome/ \
+ # -o t/lib-gpg/gpgsm_cert.p12 \
+ # --export-secret-key-p12 "committer@example.com"
+ echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \
+ --passphrase-fd 0 --pinentry-mode loopback \
+ --import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
+ gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K \
+ | grep fingerprint: | cut -d" " -f4 | tr -d '\n' > \
+ ${GNUPGHOME}/trustlist.txt &&
+ echo " S relax" >> ${GNUPGHOME}/trustlist.txt &&
+ (gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
+ echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
+ -u committer@example.com -o /dev/null --sign - 2>&1 &&
+ test_set_prereq GPGSM
;;
esac
fi
diff --git a/t/lib-gpg/gpgsm-gen-key.in b/t/lib-gpg/gpgsm-gen-key.in
new file mode 100644
index 0000000..a7fd87c
--- /dev/null
+++ b/t/lib-gpg/gpgsm-gen-key.in
@@ -0,0 +1,8 @@
+Key-Type: RSA
+Key-Length: 2048
+Key-Usage: sign
+Serial: random
+Name-DN: CN=C O Mitter, O=Example, SN=C O, GN=Mitter
+Name-Email: committer@example.com
+Not-Before: 1970-01-01 00:00:00
+Not-After: 3000-01-01 00:00:00
diff --git a/t/lib-gpg/gpgsm_cert.p12 b/t/lib-gpg/gpgsm_cert.p12
new file mode 100644
index 0000000..94ffad0
--- /dev/null
+++ b/t/lib-gpg/gpgsm_cert.p12
Binary files differ
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 25b1f8c..05d3707 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -1556,12 +1556,28 @@ test_expect_success GPG 'setup signed branch' '
git commit -S -m signed_commit
'
+test_expect_success GPGSM 'setup signed branch x509' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ git checkout -b signed-x509 master &&
+ echo foo >foo &&
+ git add foo &&
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git commit -S -m signed_commit
+'
+
test_expect_success GPG 'log --graph --show-signature' '
git log --graph --show-signature -n1 signed >actual &&
grep "^| gpg: Signature made" actual &&
grep "^| gpg: Good signature" actual
'
+test_expect_success GPGSM 'log --graph --show-signature x509' '
+ git log --graph --show-signature -n1 signed-x509 >actual &&
+ grep "^| gpgsm: Signature made" actual &&
+ grep "^| gpgsm: Good signature" actual
+'
+
test_expect_success GPG 'log --graph --show-signature for merged tag' '
test_when_finished "git reset --hard && git checkout master" &&
git checkout -b plain master &&
@@ -1581,6 +1597,27 @@ test_expect_success GPG 'log --graph --show-signature for merged tag' '
grep "^| | gpg: Good signature" actual
'
+test_expect_success GPGSM 'log --graph --show-signature for merged tag x509' '
+ test_when_finished "git reset --hard && git checkout master" &&
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git checkout -b plain-x509 master &&
+ echo aaa >bar &&
+ git add bar &&
+ git commit -m bar_commit &&
+ git checkout -b tagged-x509 master &&
+ echo bbb >baz &&
+ git add baz &&
+ git commit -m baz_commit &&
+ git tag -s -m signed_tag_msg signed_tag_x509 &&
+ git checkout plain-x509 &&
+ git merge --no-ff -m msg signed_tag_x509 &&
+ git log --graph --show-signature -n1 plain-x509 >actual &&
+ grep "^|\\\ merged tag" actual &&
+ grep "^| | gpgsm: Signature made" actual &&
+ grep "^| | gpgsm: Good signature" actual
+'
+
test_expect_success GPG '--no-show-signature overrides --show-signature' '
git log -1 --show-signature --no-show-signature signed >actual &&
! grep "^gpg:" actual
diff --git a/t/t5534-push-signed.sh b/t/t5534-push-signed.sh
index 1cea758..030331f 100755
--- a/t/t5534-push-signed.sh
+++ b/t/t5534-push-signed.sh
@@ -194,10 +194,12 @@ test_expect_success GPG 'fail without key and heed user.signingkey' '
EOF
- unset GIT_COMMITTER_EMAIL &&
- git config user.email hasnokey@nowhere.com &&
- test_must_fail git push --signed dst noop ff +noff &&
- git config user.signingkey committer@example.com &&
+ test_config user.email hasnokey@nowhere.com &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
git push --signed dst noop ff +noff &&
(
@@ -218,4 +220,57 @@ test_expect_success GPG 'fail without key and heed user.signingkey' '
test_cmp expect dst/push-cert-status
'
+test_expect_success GPGSM 'fail without key and heed user.signingkey x509' '
+ test_config gpg.format x509 &&
+ prepare_dst &&
+ mkdir -p dst/.git/hooks &&
+ git -C dst config receive.certnonceseed sekrit &&
+ write_script dst/.git/hooks/post-receive <<-\EOF &&
+ # discard the update list
+ cat >/dev/null
+ # record the push certificate
+ if test -n "${GIT_PUSH_CERT-}"
+ then
+ git cat-file blob $GIT_PUSH_CERT >../push-cert
+ fi &&
+
+ cat >../push-cert-status <<E_O_F
+ SIGNER=${GIT_PUSH_CERT_SIGNER-nobody}
+ KEY=${GIT_PUSH_CERT_KEY-nokey}
+ STATUS=${GIT_PUSH_CERT_STATUS-nostatus}
+ NONCE_STATUS=${GIT_PUSH_CERT_NONCE_STATUS-nononcestatus}
+ NONCE=${GIT_PUSH_CERT_NONCE-nononce}
+ E_O_F
+
+ EOF
+
+ test_config user.email hasnokey@nowhere.com &&
+ test_config user.signingkey "" &&
+ (
+ sane_unset GIT_COMMITTER_EMAIL &&
+ test_must_fail git push --signed dst noop ff +noff
+ ) &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ git push --signed dst noop ff +noff &&
+
+ (
+ cat <<-\EOF &&
+ SIGNER=/CN=C O Mitter/O=Example/SN=C O/GN=Mitter
+ KEY=
+ STATUS=G
+ NONCE_STATUS=OK
+ EOF
+ sed -n -e "s/^nonce /NONCE=/p" -e "/^$/q" dst/push-cert
+ ) >expect.in &&
+ key=$(cat "${GNUPGHOME}/trustlist.txt" | cut -d" " -f1 | tr -d ":") &&
+ sed -e "s/^KEY=/KEY=${key}/" expect.in >expect &&
+
+ noop=$(git rev-parse noop) &&
+ ff=$(git rev-parse ff) &&
+ noff=$(git rev-parse noff) &&
+ grep "$noop $ff refs/heads/ff" dst/push-cert &&
+ grep "$noop $noff refs/heads/noff" dst/push-cert &&
+ test_cmp expect dst/push-cert-status
+'
+
test_done
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index d7b319e..2147938 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1354,6 +1354,19 @@ test_expect_success GPG \
'test_config gpg.program echo &&
test_must_fail git tag -s -m tail tag-gpg-failure'
+# try to sign with bad user.signingkey
+test_expect_success GPGSM \
+ 'git tag -s fails if gpgsm is misconfigured (bad key)' \
+ 'test_config user.signingkey BobTheMouse &&
+ test_config gpg.format x509 &&
+ test_must_fail git tag -s -m tail tag-gpg-failure'
+
+# try to produce invalid signature
+test_expect_success GPGSM \
+ 'git tag -s fails if gpgsm is misconfigured (bad signature format)' \
+ 'test_config gpg.x509.program echo &&
+ test_config gpg.format x509 &&
+ test_must_fail git tag -s -m tail tag-gpg-failure'
# try to verify without gpg:
diff --git a/t/t7030-verify-tag.sh b/t/t7030-verify-tag.sh
index 291a1e2..99f35a5 100755
--- a/t/t7030-verify-tag.sh
+++ b/t/t7030-verify-tag.sh
@@ -41,6 +41,13 @@ test_expect_success GPG 'create signed tags' '
git tag -uB7227189 -m eighth eighth-signed-alt
'
+test_expect_success GPGSM 'create signed tags x509 ' '
+ test_config gpg.format x509 &&
+ test_config user.signingkey $GIT_COMMITTER_EMAIL &&
+ echo 9 >file && test_tick && git commit -a -m "nineth gpgsm-signed" &&
+ git tag -s -m nineth nineth-signed-x509
+'
+
test_expect_success GPG 'verify and show signatures' '
(
for tag in initial second merge fourth-signed sixth-signed seventh-signed
@@ -72,6 +79,13 @@ test_expect_success GPG 'verify and show signatures' '
)
'
+test_expect_success GPGSM 'verify and show signatures x509' '
+ git verify-tag nineth-signed-x509 2>actual &&
+ grep "Good signature from" actual &&
+ ! grep "BAD signature from" actual &&
+ echo nineth-signed-x509 OK
+'
+
test_expect_success GPG 'detect fudged signature' '
git cat-file tag seventh-signed >raw &&
sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
@@ -112,6 +126,13 @@ test_expect_success GPG 'verify signatures with --raw' '
)
'
+test_expect_success GPGSM 'verify signatures with --raw x509' '
+ git verify-tag --raw nineth-signed-x509 2>actual &&
+ grep "GOODSIG" actual &&
+ ! grep "BADSIG" actual &&
+ echo nineth-signed-x509 OK
+'
+
test_expect_success GPG 'verify multiple tags' '
tags="fourth-signed sixth-signed seventh-signed" &&
for i in $tags
@@ -125,6 +146,19 @@ test_expect_success GPG 'verify multiple tags' '
test_cmp expect.stderr actual.stderr
'
+test_expect_success GPGSM 'verify multiple tags x509' '
+ tags="seventh-signed nineth-signed-x509" &&
+ for i in $tags
+ do
+ git verify-tag -v --raw $i || return 1
+ done >expect.stdout 2>expect.stderr.1 &&
+ grep "^.GNUPG:." <expect.stderr.1 >expect.stderr &&
+ git verify-tag -v --raw $tags >actual.stdout 2>actual.stderr.1 &&
+ grep "^.GNUPG:." <actual.stderr.1 >actual.stderr &&
+ test_cmp expect.stdout actual.stdout &&
+ test_cmp expect.stderr actual.stderr
+'
+
test_expect_success GPG 'verifying tag with --format' '
cat >expect <<-\EOF &&
tagname : fourth-signed
diff --git a/t/t7510-signed-commit.sh b/t/t7510-signed-commit.sh
index 6e2015e..4e37ff8 100755
--- a/t/t7510-signed-commit.sh
+++ b/t/t7510-signed-commit.sh
@@ -227,4 +227,11 @@ test_expect_success GPG 'log.showsignature behaves like --show-signature' '
grep "gpg: Good signature" actual
'
+test_expect_success GPG 'check config gpg.format values' '
+ test_config gpg.format openpgp &&
+ git commit -S --amend -m "success" &&
+ test_config gpg.format OpEnPgP &&
+ test_must_fail git commit -S --amend -m "fail"
+'
+
test_done