Updated
I have posted a script I'm using for this to the StackExchange Code Review site.
My original question for this was Is there a way I can sign a Git commit with an X.509 certificate and timestamp?. For a while I thought I could only get things I've signed with my X.509 certificate timestamped by a trusted third party. This is not the case. Digital signing with an X.509 certificate and trusted time stamping are mutually exclusive. I have updated my question to reflect this.
As pointed out by VonC, signing Git commits with an X.509 certificate doesn't add any value. Using a GPG key is a much better option because of Git's built in support.
I have accepted Greg's answer because it's the closest to what I was asking for, even though my original question was a bit ambiguous. As Greg points out, if you can prove you knew a commit hash at a certain point in time, that guarantees you knew the repository content the hash is for at that time and there's no need to store any extra data in the repository. The timestamp data can be stored anywhere.
It's possible to use openssl
(v1.0.0+) and curl
to request RFC3161 timestamps for commit hashes.
Request A Timestamp
You'll need to have a bit of info for this:
- URL - An RFC3161 time-stamping service
- REV - The revision (hash) you want a timstamp for. Must be a full hash.
CONTENT_TYPE="Content-Type: application/timestamp-query"
ACCEPT_TYPE="Accept: application/timestamp-reply"
openssl ts -query -cert -digest "$REV" -sha1 \
| curl -s -H "$CONTENT_TYPE" -H "$ACCEPT_TYPE" --data-binary @- $URL
The above will output the signed timestamp to stdout
. It may also output an error if the timestamp service refuses the request.
Verify A Timestamp
This is very similar to requesting a timestamp, but you also need:
- CAFILE - A certificate chain from the timestamp service back to a root CA
The time-stamping service should be signing timestamps with a certificate that was issued by a trusted authority. If not, your timestamps don't have much credibility. If you can't find or create a proper certificate chain, try using the cacert.pem
published by curl
. It's here.
The below snippet assumes an existing, signed timestamp reply is being passed to stdin
. It should be possible to pipe the above request directly into the below verify command. If you store the response from the request in a variable it may be necessary to base64 encode / decode it (man base64
).
openssl ts -verify -digest "$REV" -in /dev/stdin -CAfile "$CAFILE"
If you examine a reply, you'll notice the request digest matches the Git revision that was used. You can examine a plain text version of a reply with this command.
openssl ts -reply -in /dev/stdin -text
Here is an example of a reply where I've added the Git revision at the top.
--------------------------------------------------------------------------------
Revision: 871d715e5c072b1fbfacecc986f678214fa0b585
--------------------------------------------------------------------------------
Status info:
Status: Granted.
Status description: unspecified
Failure info: unspecified
TST info:
Version: 1
Policy OID: 1.3.6.1.4.1.6449.2.1.1
Hash Algorithm: sha1
Message data:
0000 - 87 1d 71 5e 5c 07 2b 1f-bf ac ec c9 86 f6 78 21 ..q^\.+.......x!
0010 - 4f a0 b5 85 O...
Serial number: 0xB2EA9485C1AFF55C6FFEDC0491F257C8393DB5DC
Time stamp: Aug 15 08:41:48 2012 GMT
Accuracy: unspecified
Ordering: no
Nonce: 0x615F0BF6FCBBFE23
TSA: DirName:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO Time Stamping Signer
Extensions:
Other Notes
A lot of time-stamping services ask users to add a delay to scripted signing requests. Make sure you find out if the service you plan to use requires it. At the time of writing the one I'm using, Comodo, asks for a 15 second delay between scripted requests. The only reason I chose to use Comodo is because that's who I bought my code signing certificate from.
Git notes seems like the obvious choice for storing signed timestamp replies, but I don't quite have a complete solution to post. I'm stuck on this at the moment.
My original question and updates are below.
I would like to be able to prove when my Git commits are happening and that the history of my repository hasn't been re-written. It doesn't have to be every commit. Once a day or once a week would be sufficient. Is there a recommended way to do so?
I know I can sign Git commits with a GPG key, but I'm wondering if there's a way I can sign my commits with an X.509 certificate and the use of an online time-stamping service like http://timestamp.comodoca.com/rfc3161.
If not, would dumping the current revision using git rev-parse --verify HEAD
into a text file once a day, signing that file, and committing be sufficient to prove (roughly) when my code was written?
Added Info For Clarity
I know that Git guarantees the integrity of a repository, but, as far as I understand, if I control the repository a third party would have to trust that I haven't re-written the history of the repository or rolled my clock back and created a completely fake repository just to 'prove' my code is older than it actually is? I also don't want to publish my repository publicly.
Here's a fictional use cases that should give a better idea of what I want to do.
I publish some code online. A year later someone copies and publishes the same code in a book or article and claims I was the one that copied them. At that point, I would like to be able to take my repository and prove that I committed that code a year ago, before they re-published it.
By using an X.509 certificate with a time-stamping service I can prove when the signing occurred. As long as I can prove I knew the hash for the year old commit, Git guarantees the integrity of the archive.
Alternatively, is there a way to sign a commit using a GPG key, but with a verified time-stamp? Is there a trusted third party that provides a time-stamping service similar to the ones available for X.509 certificates, but for GPG?
Maybe I could use a combination of a GPG key and an X.509 certificate. Assuming I keep a copy of my (public) GPG key in the repository, would the following work if I did it at the end of each day?
- Sign my (public) GPG key using my X.509 certificate and an online time-stamping service.
- Commit the change to the repository with a signature from my (private) GPG key.
All you have to do, is publish the SHA1 (the commit id) publicly. If you like, you can take that SHA1 and sign it with your X.509 certificate (using an appropriate timestamping service) and keep that around. If anybody challenges your authorship, you can easily show that you knew the contents of the repository at the particular time that generated that particular SHA1. You don't need to actually store any signature inside the code repository.