-
Notifications
You must be signed in to change notification settings - Fork 2
X509 vs OpenPGP
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.
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
- Discussion on the Rubygems mailing list from 2011: http://rubyforge.org/pipermail/rubygems-developers/2011-May/006598.html
- Proof of concept plugin for Rubygems that uses GPG (by shelling out): https://github.com/grant-olson/rubygems-openpgp
- Information about signing jar files: http://docs.oracle.com/javase/tutorial/deployment/jar/signing.html
- A Ruby wrapper around various cryptography algorithms, including X509: https://github.com/krypt/krypt
- Java library (e.g. for jruby) for various cryptography algorithms: http://www.bouncycastle.org/java.html
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
- Wikipedia article: https://en.wikipedia.org/wiki/X.509
- RFC: http://tools.ietf.org/html/rfc2510 (there are multiple ones)
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:
- Maven uses X509: http://mrhaki.blogspot.com/2009/05/development-jar-signing-in-maven.html
- Available on Jruby by installing
jruby-openssl
, see https://github.com/jruby/jruby/tree/master/gems/jruby-openssl for more information - The current implementation in Rubygems creates a self signed X509 certificate without a password
- https://gist.github.com/8e0f4e8d56fbaf90d387 https://gist.github.com/a665b156386773a9bdf7
- Small guide on using the various parts of OpenSSL/X509: http://www.madboa.com/geek/openssl/
- Wikipedia article: https://en.wikipedia.org/wiki/Pretty_Good_Privacy
- RFC: https://tools.ietf.org/html/rfc4880
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:
- Perl uses PGP (optionally): https://metacpan.org/module/CPAN#Cryptographically-signed-modules
- Python also includes optional support for PGP: http://docs.python.org/2/distutils/uploading.html
- Sonatype (one of the major maven repos) uses PGP: http://www.sonatype.com/people/2012/03/the-first-line-of-defense-checksums-and-pgp-signatures-in-repositories/
- Available Ruby implementation (though a partial one): https://github.com/singpolyma/openpgp
- Another implementation: https://github.com/ueno/ruby-gpgme (relies on GPGME, which at the moment does not support 64-bit Windows)
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.