X509 vs OpenPGP

YorickPeterse edited this page Feb 5, 2013 · 51 revisions

This page discusses the various advantages/disadvantages of using X509 or OpenPGP for signing in Rubygems. Please join #rubygems-trust on Freenode if you want to join the discussion.

Requirements

Regardless of the solution the following things are required:

  • It should work on the various Ruby implementations such as MRI, Jruby, Rubinius, etc
  • It should be easy to set up, a developer should be able to get started in 10-15 minutes
  • It should be commonly available or easy to install if it's not
  • It should work on the common OS' such as as Mac OS X, Windows and the various Linux distributions

Existing Resources

Concerns around bundled certificates

A common misconception seems to be that it's a good idea to distribute signing related information (e.g. the public key) in a Gem itself. Using such a system is not a very good idea since it introduces a circular dependency: to verify an unverified Gem you must first unpack it, then verify it. Because one should assume that unpacking a Gem can potentially lead to your system going boom this isn't a very good solution.

Couldn't we just check the signature before executing any code from the gem? - jstr

A better solution would be to store signatures and public keys in a separate location (e.g. a separate API). Preferably also on a different server that is isolated and well secured. The RubyGems CLI would provide the means for retrieving and using this information. This would mean you'd have to do something along the lines of the following to sign and push a Gem:

$ gem sign example-1.0.0.gem ./signatures/example-1.0.0.gem.sig
$ gem push example-1.0.0.gem --signature=./signatures/example-1.0.0.gem.sig
Pushing example-1.0.0.gem with the provided signature...

Note: when I talk about storing signatures and the likes inside a Gem I'm talking about the following file structure:

. 
|_ example-1.0.0.gem
  |_ data.tar.gz
  | |_ example-1.0.0.gem.sig
  |
  |_ metadata.tar.gz

When downloading a Gem you should assume it's a bomb waiting to explode, you don't want to even touch it until you're absolutely sure it's safe. The structure above would make this impossible as you first have to extract the Gem in order to be able to verify it.

Update: apparently I (Yorick) am overly paranoid so take the above statement with a grain of salt.

Note that signatures are stored outside data.tar.gz and metadata.gz in the current implementation: https://github.com/rubygems/rubygems/blob/master/lib/rubygems/package/tar_writer.rb#L157-L186

X509

Advantages:

  • Already supported in pure Ruby by using the OpenSSL library

Disadvantages:

  • Probably requires more work to set up a proper infrastructure
  • Not installed by default on Windows (but required for many popular applications like Rubygems with rubygems.org as a source over HTTPS, or Ruby on Rails)
  • Apple is deprecating OpenSSL on OS X in favour of Common Crypto meaning the use of X509 might become troublesome in the near future (only hypothetical at this point)
  • Not distributed: people need to trust/sign a single CA (RubyGems in this case) similar to how it works with SSL certificates. However multiple CAs could be supported for alternative sources.

Notes:

OpenPGP

Advantages:

  • Existing infrastructure (key servers) is extremely easy to use.
  • Existing set of tools and proper documentation
  • Distributed: people can verify and sign each others keys and public keys can be distributed amongst key servers easily

Disadvantages:

  • Not installed by default on Windows and OS X
  • If a system-level GPG library is not bundled with Ruby or RubyGems, a pure-Ruby implementation of OpenPGP would be required, which would be non-trivial to vet as secure

Notes:

Fallbacks

Whatever the solution, there should be a fall-back in case the requirements for the verification process can not be met (e.g. OpenSSL is not installed). When this is the case the user should be notified that Gem signing is not enabled due to missing dependencies.