But if you want to get my key you have to get into my dev machine. There is a class of people that can do that, but it substantially raises the bar. Really what I would like is to make it so my Github account only accepts signed commits, so that it is impossible to impersonate me without my key.
Linus' point is that you don't need to validate the entire code base, if you have one signed commit then everything that is "below" is also (implicitly) signed. For example you can have a feature branch that has many small commits and one merge commit that joins feature branch with master and you just need to sign the merge commit to make sure all of these small commits are OK.
And Linus is missing the point entirely.
Yes, signing a commit implicitly signs the repo all the way back to its beginning. But when I'm actually performing the action of signing this commit, I am not actually going back through every commit and verifying them.
The fact that you can use the properties of commit hashes to verify a chain all the way back to the origin of the repo is irrelevant, because that's not what someone actually signing a commit is intending or should be expected to do.
When I'm signing a commit, I'm vouching that I am responsible for applying this set of changes to a given state of a repo. It says nothing about my belief in the validity of changes to the repo made before it.
The above example still makes sense, even given your point about the mild absurdity of the idea that the entire repo is implicitly trusted.
Say you relax the restrictions such that only the first parent of every commit must be signed. While this might make sense for a small branch edited by one person, what if it was a long-lived branch modified by multiple people?
The only case I can see for such a system is if you're doing a pull-request based workflow where your code review tool (GHE, Bitbucket, whatever) is the only tool capable of updating `master` and produces signed merge commits whenever a branch passes code review. Then you can at least (in theory) attest that all commits to master have been reviewed.
But if not that, and people are signing their commits to master anyway (which you can automate in your `~/.gitconfig`, why allow exemptions to commits on branches?
Then you should be signing a diff not a commit. Signing a commit represents something different, because commit references both the current tree (all files) and parent commit you're basically vouching for all history and all files.
For reference, git commit structure [0]:
$ git cat-file -p ca82a6dff817ec66f44342007202690a93763949
tree cfda3bf379e4f8dba8717dee55aab78aef7f4daf
parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
author Scott Chacon <[email protected]> 1205815931 -0700
committer Scott Chacon <[email protected]> 1240030591 -0700
changed the version number
[0]: https://git-scm.com/book/en/v2/Git-Internals-Transfer-Protoc...Similarly, if I sign a commit containing `parent 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7`, that says nothing about whether I consider all previous commits to be valid. It only says that the commit I just signed was created based off of that parent commit, and that in order to see the changes I actually made you'll need to diff against that parent. (Which `git show <my commit id>` will do automatically.)
Note that even with signed commits there are same gaps in the system. For example without signed pushes someone can take your signed commit from branch "test" an push it to "master".
Just because a signing a commit can be construed as the author attesting to the authenticity of every single bit in the a repo's entire commit history doesn't mean it should, doesn't mean it's what the signing party actually intended, and doesn't mean that such an inference even remotely sane. Such an expectation is utterly absurd on its face for any repo with more than a handful of commits and/or authors.
Further, git does not have a mechanism for signing either diffs or diffs plus the `tree` of the prior commit plus all of the other commit metadata. Even if it did, there's now the problem that by excluding the prior commit, you now have no way of ensuring that signed commits are properly ordered if they happen to share `tree`s (e.g., in the case of a revert). This is a patently terrible property from a cryptographic perspective.
Given the above, there is literally no native way in git to achieve the semantics of signing a singular diff. Combined with the complete absurdity of assuming a signed commit is attesting to tens of thousands of commits or more from dozens of authors or more preceding it, signing commits is the most natural way to express the useful semantic of "I made this change to this tree".
The semantics you suggest are totally unrealistic and virtually useless in practice, so what exactly is the point of your argument?
Our PGP keys don't really change that often.
>> Signing each commit is totally stupid. It just means that you automate it, and you make the signature worth less.
I've never bought that argument.[0] You should use whatever method actually addresses your threat model. If you're going to sign every commit (I do), then commit (no pun intended) to doing so properly. I use `git add -p` for every commit and review my changes, and enter my smartcard PIN for every single commit. I use it to assert my identity on those commits. If you automate signing past commits, then you open yourself up to attack. Saying "this idea is stupid because everyone is going to do it wrong" is not the basis of a strong argument.
The other part of his argument is that the DAG itself allows you to just sign a single commit to verify the entire history. Firstly: that's different than signing each commit, which asserts identity of the author. Secondly, which has always been my argument, is that it rests on the security of SHA-1, which has been under question for quite a while, and has since been demonstrably broken. Are second preimage attacks unlikely with SHA-1? At least today, yes. Consider your threat model. Linus didn't even do that. He said long ago that SHA-1 was intended for integrity, not security, so retrofitting it in an argument is also not useful.
[0]: https://mikegerwitz.com/papers/git-horror-story#merge-3
([0] from 2012 doesn't address the issue in this thread---expired or revoked keys.)
For me it's not a problem that old commits are signed with revoked keys, there are several ways one can get old broken signatures (e.g. key rotation) and by design they should not be trusted.
One interesting voice in signing all commits by Linus:
> Signing each commit is totally stupid. It just means that you automate it, and you make the signature worth less. It also doesn't add any real value, since the way the git DAG-chain of SHA1's work, you only ever need _one_ signature to make all the commits reachable from that one be effectively covered by that one. So signing each commit is simply missing the point.
Source: http://git.661346.n2.nabble.com/GPG-signing-for-git-commit-t...
Also, depending one one's threat model signing all commits can still be not sufficient: https://github.com/git/git/commit/a85b377d0419a9dfaca8af2320...