How can I use RFC3161 (trusted) timestamps to prove the age of commits in my Git repository?

4.9k views Asked by At

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?

  1. Sign my (public) GPG key using my X.509 certificate and an online time-stamping service.
  2. Commit the change to the repository with a signature from my (private) GPG key.
5

There are 5 answers

1
Greg Hewgill On BEST ANSWER

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.

2
Philip Oakley On

Simply add a time stamped certificate to your latest commit. The sha1 will verify that the certificate hasn't been modified, and the certificate itself will verify all those 'facts' that it claims, such as the date and time stamp from a trusted server, and who you claim to be, etc. That is, the commit signs the certificate, as per VonC's quote from Linus's speech ;-)


[EDIT] Obviously you do need to publish the sha1 of that new commit, otherwise you could amend it (and the certificate) and use the new sha1 to claim whatever is in that new commit's history. As always it is creating the web of mutual trust.

9
VonC On

I am not usre I follow, because Linus did make Git with that specific feature in mind (ie the integrity of what you are putting in is exactly what comes out)

See is 2007 speech at Google (transcript):

Most of them I could discard without even trying them out.

  • If you're not distributed, you are not worth using, it's that simple.
  • If you perform badly, you are not worth using, it is that simple.
  • And if you cannot guarantee that the stuff I put into an SCM comes out exactly the same, you are not worth using.

Quite frankly, that pretty much took care of everything out there.

There are a lot of SCM systems that do not guarantee that what you get out of it again is the same thing you put in.
If you have a memory corruption, if you have a disc corruption, you may never know.
The only way you know is you notice that there is corruption in the files when you check them out. And the source control management system does not protect you at all.
And this is not even uncommon. It is very very common. .

So I don't think adding another integrity feature will add any value.
And the "timestamp" isn't exactly a good idea either, since they aren't recorded for a DVCS in the first place (see "Git: checking out old file WITH original create/modified timestamps", and "What's the equivalent of use-commit-times for git?")

0
matthias_buehlmann On

I wrote an article that goes into the details of timestamping a git repository using RFC3161 timestamps and also provide a post-commit hook that automatically does this:

https://matthias-buehlmann.medium.com/git-as-cryptographically-tamperproof-file-archive-using-chained-rfc3161-timestamps-ad15836b883

The key points are the following ones:

  1. SHA1 should probably not be considered secure anymore, especially if it is to prove something in a couple of years from now (https://sha-mbles.github.io/). Thus, it is advisable to initialize the git repository using sha256 hashes git init --object-type=sha256 (even if sha256 support is still considered experimental)

  2. There are benefits of storing the timestamp (and LTV data) together with the repository - especially in order to arbitrarily extend the lifetime of the timestamp tokens as well as to protect them from becoming invalid due to leaked private keys, by adding CRL data of previous timestamps to the repository and then timestamping it using a new timestamp.

  3. Git is very well suited for timestamping using RFC3161, which gives it a tamperproof evicdence record of all data changes.

0
Zhenzhen Zhan On

I just wrote a program called "GitLock" exactly for this purpose. It adds trusted timestamps to your repo.

https://www.npmjs.com/package/gitlock