What data is being signed when you `git commit --gpg-sign=<key-id>`? What data is being signed when you `git commit --gpg-sign=<key-id>`? git git

What data is being signed when you `git commit --gpg-sign=<key-id>`?


After reading the code in commit_tree_extended, it seems the data used to sign is the part from "tree" to the end of the comment, of course excluding the signature.

In your example, it should be:

tree 70e7c184c3a89c749174b4987830c287fd78952dauthor Dan Neumann <danrodneu@gmail.com> 1399683715 -0500committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500Initial commit

From the git source:

Init of buffer:

strbuf_init(&buffer, 8192); /* should avoid reallocs for the headers */strbuf_addf(&buffer, "tree %s\n", sha1_to_hex(tree));

Parent commits traversing:

/** NOTE! This ordering means that the same exact tree merged with a* different order of parents will be a _different_ changeset even* if everything else stays the same.*/while (parents) {    struct commit_list *next = parents->next;    struct commit *parent = parents->item;    strbuf_addf(&buffer, "parent %s\n",    sha1_to_hex(parent->object.sha1));    free(parents);    parents = next;}

Person/date information:

if (!author)    author = git_author_info(IDENT_STRICT);strbuf_addf(&buffer, "author %s\n", author);strbuf_addf(&buffer, "committer %s\n", git_committer_info(IDENT_STRICT));if (!encoding_is_utf8)    strbuf_addf(&buffer, "encoding %s\n", git_commit_encoding);while (extra) {    add_extra_header(&buffer, extra);    extra = extra->next;}strbuf_addch(&buffer, '\n');

The comment & encoding check:

/* And add the comment */strbuf_addbuf(&buffer, msg);/* And check the encoding */if (encoding_is_utf8 && !verify_utf8(&buffer))    fprintf(stderr, commit_utf8_warn);

That's where the signing happens. Signature will be added after the header.

if (sign_commit && do_sign_commit(&buffer, sign_commit))    return -1;

There would be parent information too if your commit had some.


This answer is a work in progress.

Background

First, some ruminations on the problems with the current Git signature mechanism.

Ideally, Git would use one of GnuPG's built-in signing mechanisms. If it did so, then it would be easy verify Git commits without having to invoke Git or to write scripts, by simply using GnuPG's gpg --verify or gpg2 --verify.

In this respect, it is a pity that Git did not adopt GnuPG's "detached signature" signing mechanism, as proposed on the Linux Kernel Mailing List in 2005. More recently, Owen Jacobson has listed some additional reasons why detached signatures would be desirable over Git's current approach. He points out that currently:

  • Signatures are embedded within the objects they sign. The signature is part of the object's identity; since Git is content-addressed, this means that an object can neither be retroactively signed nor retroactively stripped of its signature without modifying the object's identity. Git's distributed model means that these sorts of identity changes are both complicated and easily detected.

  • Commit signatures are second-class citizens. They're a relatively recent addition to the Git suite, and both the implementation and the social conventions around them continue to evolve.

  • Only some objects can be signed. While Git has relatively weak rules about workflow, the signature system assumes you're using one of Git's more widespread workflows by limiting your options to at most one signature, and by restricting signatures to tags and commits (leaving out blobs, trees, and refs).

Mike Gerwitz points out one of the most serious ramifications of Git's current approach. Git commits have both a "committer" and an "author" field, allowing for the committer and author of a commit to be two separate people. However, Git commits currently allow the inclusion of only one signature. So, whose signature should it be? Ideally, both the author and the committer would be able to sign the commit. Detached signatures would allow this. So would nested in-line signatures, for that matter. But because Git does not use either of these options, it forces us to choose between two unsatisfactory options:

  1. The committer strips the author's signature and signs a commit themselves.

  2. The committer declines to sign the commit.

That summarises the bad news. The good news is that Git's wrapper for GnuPG does at least include the --show-signature option for git log, which verifies signatures using GnuPG. This allows the Git log to show whether or not a signature was made by a key bearing a UID that you have placed trust in.

If it is, GnuPG will show:

Good signature from "John Doe <john.doe@example.com>"

If not, GnuPG will show:

Good signature from "John Doe <john.doe@example.com>"WARNING: This key is not certified with a trusted signature!         There is no indication that the signature belongs to the owner.

Your question

As indicated in Poko's answer, your commit appears, on the face of it, to be equivalent to the following clearsigned document:

-----BEGIN PGP SIGNED MESSAGE-----Hash: SHA1tree 70e7c184c3a89c749174b4987830c287fd78952dauthor Dan Neumann <danrodneu@gmail.com> 1399683715 -0500committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500Initial commit-----BEGIN PGP SIGNATURE-----Version: GnuPG v1iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9BwercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJuCKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610hhkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGFKl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgYBOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w==sRee-----END PGP SIGNATURE-----

However, suppose we save that as danneau_stackoverflow_example.asc and attempt to verify it, here's the output:

$ gpg --verify danneau_stackoverflow_example.asc gpg: Signature made Sat 10 May 2014 02:01:55 BSTgpg:                using RSA key C7AA5FC37E482429gpg: Can't check signature: public key not found

What GnuPG means by this is that because I don't have your public key, it can't actually tell whether the signature is good or bad. So, even if I tamper with the contents, I get the same output:

$ echo "-----BEGIN PGP SIGNED MESSAGE-----Hash: SHA1tree 70e7c184c3a89c749174b4987830c287fd78952dauthor Dan Neumann <danrodneu@gmail.com> 1399683715 -0500committer Dan Neumann <danrodneu@gmail.com> 1399683715 -0500EVIL MESSAGE HERE-----BEGIN PGP SIGNATURE-----Version: GnuPG v1iQEcBAABAgAGBQJTbXqDAAoJEMeqX8N+SCQpTBIH/3zCpf0w0+xp8hkwz7dTV9BwercZp4UpxKV1HgqCxu2r/nGIuZyabLwTis1rcwXOVC4DgRxO0f2BiP0xnyL3OhJuCKh8l+HZvvGqVH3Dopm0D/kOxDAWHcjokbyzWBbYJX6WhvT8OI7SSYmwuF4r610hhkZ1xgjo4p1x9WegY296PzA1wEe6yy9BvvdIpJHoqBVKClgFrZvtE5PidbrAyLGFKl/2f0K3peBdo6XP0Zaml8NyQlFmAlCV831hHgUmZsBSRpgh/WNvrDSNILTlFJgYBOPb2yPP+tiJOXYB66MsjQY9GlX7n43miu5wMtdk1AGqh+26OExbSrZcYVFLk4w==sRee-----END PGP SIGNATURE-----" | gpg --verify -gpg: Signature made Sat 10 May 2014 02:01:55 BSTgpg:                using RSA key C7AA5FC37E482429gpg: Can't check signature: public key not found

So this means that Poko might not have correctly figured out what exactly you signed after all.

In order to get to the bottom of this, I have tried converting commits, signed by private keys whose corresponding public keys I do possess, into clearsigned files, and passing them to GnuPG to verify. So far, I have only ever received "Bad signature" responses. If I figure out where I am going wrong, I will update this answer.