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

5

20

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.
Mesognathous answered 11/8, 2012 at 8:32 Comment(6)
What's the point? As long as no one is messing with the local repo on your machine, the integrity of the history is guaranteed. Even if you pull changes from someone who's rewritten history, the changes won't be merged automatically and both copies of the history stay in the repo. If you want one definitive copy of the repo with no rewritten history, you could have everyone push their work to a server somewhere that rejects forced pushes or something like that...Bra
I would like to be able to prove to others that I haven't rewritten the history of the repository.Mesognathous
Just allow them to pull your repo periodically. If you rewrite history, sha1 will change, they will know it.Insusceptible
@Insusceptible one might want to prove to authorities at some date in the future that commits existed at a certain point in time and were not rewritten later - for example during an audit. So "allowing them to pull your repo periodically" is not a solution. storing timestamp tokens of a trusted TSA for commit hashes seems the proper way of doing this, the question is what's the most elegant way of doing thatDeodorize
@Deodorize If we trust auditors, then it's their responsibility to pull and keep track of what was pulled and then. If not, then TSA helps.Insusceptible
@Insusceptible these are auditors of the future - they may not even exist at the time you create the commitDeodorize
L
7

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.

Laubin answered 17/8, 2012 at 8:30 Comment(1)
it really depends on the policies set by the entity whom one wants to prove it to. The entity may have specified that data must be timestamped using a specific TSA service. If that's the case then te SHA1 commit hash can be timestamped by requesting an RFC3161 token for it (no need to additionally sign it using a X.509 certificate, the timestamp is signed by the trusted TSA certificate). However, one also needs to check whether the policies employed accept SHA1 as a secure hashing algorithm for proofs. If not, one must not rely on the commit hash (or use a --object-type=sha256 repo)Deodorize
K
3

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.

Kenleigh answered 11/8, 2012 at 22:9 Comment(2)
publishing of the hash is not required if it is timestamped (also signature using a certificate is not required). The trusted timestamp proves to anyone who trusts the TSA that the hash (and thus the commit with all its data and history) existed at the time certified in the timestampDeodorize
@Deodorize ut to clarify, the key is to choose which of the two 'hashes' (the commit or the time stamp) is the reference item and which is secondary content contained within the reference item. Either the timestamp certifies the commit, or the commit certifies the timestamp. Then you 'publish' the reference so other know and trust them.Kenleigh
D
2

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.

Deodorize answered 22/2, 2021 at 16:22 Comment(0)
N
1

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?")

Nugget answered 11/8, 2012 at 20:3 Comment(9)
I trust Git for the integrity, but (eventually) I'd like to be able to let someone look at my (private) repository and verify that certain code is X years old. I updated the question to make it more clear.Mesognathous
@RyanJ isn't what the "author date" associated with any new commit is for? No need for a new timestamp: the commit already contain all the information you need.Nugget
That time-stamp isn't verified by a third party, is it? I assume it's generated locally, so it's based on my word, not something provable. Using a time-stamp that comes from Comodo, Verisign, etc.. means a third-party has guaranteed it's accurate. When I sign and time-stamp something with my X.509 certificate, the certificate authorities' time-stamp service shows up as a countersignature. For example, when I request a time-stamp from timestamp.comodoca.com/authenticode, the countersignature is from COMODO Time Stamping Signer.Mesognathous
"That time-stamp isn't verified by a third party, is it?" True, that would be an issue which isn't addressed by any DVCS, because of their distributed nature and the lack of guarantee to access a specific third-party service when doing the commit (ie, the tool git itself cannot provide that feature).Nugget
Assuming I can check out a time-stamped file and verify it independent of Git, does Git guarantee the integrity of every commit before the one I checked out? To put it another way, does a commit hash represent the integrity of the entire repository or only the part of the tree the commit depends on?Mesognathous
@RyanJ a commit hash represent the integrity of itself, its content and all its ancestors. If we are talking about the commit of the root directory of the repo, then a single commit SHA1 represents the integrity of the all repo. Would you add to a commit additional data (in its comment), you wouldn't be able to change those data without changing the SHA1. You could also add that information later, using git notes (alblue.bandlem.com/2011/11/git-tip-of-week-git-notes.html), but it has its own separate SHA1 to remember (ie the SHA1 of the root commit isn't enough anymore).Nugget
let us continue this discussion in chatMesognathous
This is hopefully my last question anyway. I appreciate your help and I think I almost understand, but I don't quite understand what you mean by a commit of the root directory of the repository. What if I create an orphaned branch using git checkout --orphan MYBRANCH? Would the hashes from commits on MYBRANCH still verify the integrity of other branches? I've been trying to read this (git-scm.com/book/ch9-2.html), but it's a bit over my head.Mesognathous
@RyanJ true, an orphan branch would introduce another SHA1, independent for the root tree SHA1 (even though #1384825 suggests to create a new repo instead ;) ).Nugget
P
0

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

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

Preuss answered 8/6, 2016 at 20:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.