[前][次][番号順一覧][スレッド一覧]

ruby-changes:27074

From: drbrain <ko1@a...>
Date: Thu, 7 Feb 2013 15:05:57 +0900 (JST)
Subject: [ruby-changes:27074] drbrain:r39126 (trunk): * lib/rubygems/package.rb: Ensure digests are generated for signing.

drbrain	2013-02-07 14:56:53 +0900 (Thu, 07 Feb 2013)

  New Revision: 39126

  http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=39126

  Log:
    * lib/rubygems/package.rb:  Ensure digests are generated for signing.
    * test/rubygems/test_gem_package.rb:  Test for the above.
    
    * lib/rubygems/security/policy.rb:  Ensure digests are present when
      verifying a gem and match the number of signatures bidirectionally.
    * test/rubygems/test_gem_security_policy.rb:  Test for the above.
    
    * lib/rubygems.rb:  Documentation improvements (by zzak)

  Modified files:
    trunk/ChangeLog
    trunk/lib/rubygems/package.rb
    trunk/lib/rubygems/security/policy.rb
    trunk/lib/rubygems.rb
    trunk/test/rubygems/test_gem_package.rb
    trunk/test/rubygems/test_gem_security_policy.rb

Index: ChangeLog
===================================================================
--- ChangeLog	(revision 39125)
+++ ChangeLog	(revision 39126)
@@ -1,3 +1,14 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1
+Thu Feb  7 14:56:15 2013  Eric Hodel  <drbrain@s...>
+
+	* lib/rubygems/package.rb:  Ensure digests are generated for signing.
+	* test/rubygems/test_gem_package.rb:  Test for the above.
+
+	* lib/rubygems/security/policy.rb:  Ensure digests are present when
+	  verifying a gem and match the number of signatures bidirectionally.
+	* test/rubygems/test_gem_security_policy.rb:  Test for the above.
+
+	* lib/rubygems.rb:  Documentation improvements (by zzak)
+
 Thu Feb  7 05:52:00 2013  Zachary Scott  <zachary@z...>
 
 	* doc/pty/README: Remove static documentation file
Index: lib/rubygems/package.rb
===================================================================
--- lib/rubygems/package.rb	(revision 39125)
+++ lib/rubygems/package.rb	(revision 39126)
@@ -277,9 +277,13 @@ EOM https://github.com/ruby/ruby/blob/trunk/lib/rubygems/package.rb#L277
   # the security policy.
 
   def digest entry # :nodoc:
-    return unless @checksums
+    algorithms = if @checksums then
+                   @checksums.keys
+                 else
+                   [Gem::Security::DIGEST_NAME]
+                 end
 
-    @checksums.each_key do |algorithm|
+    algorithms.each do |algorithm|
       digester = OpenSSL::Digest.new algorithm
 
       digester << entry.read(16384) until entry.eof?
Index: lib/rubygems/security/policy.rb
===================================================================
--- lib/rubygems/security/policy.rb	(revision 39125)
+++ lib/rubygems/security/policy.rb	(revision 39126)
@@ -152,8 +152,8 @@ class Gem::Security::Policy https://github.com/ruby/ruby/blob/trunk/lib/rubygems/security/policy.rb#L152
   end
 
   def inspect # :nodoc:
-    "[Policy: %s - data: %p signer: %p chain: %p root: %p " +
-      "signed-only: %p trusted-only: %p]" % [
+    ("[Policy: %s - data: %p signer: %p chain: %p root: %p " +
+     "signed-only: %p trusted-only: %p]") % [
       @name, @verify_chain, @verify_data, @verify_root, @verify_signer,
       @only_signed, @only_trusted,
     ]
@@ -177,11 +177,16 @@ class Gem::Security::Policy https://github.com/ruby/ruby/blob/trunk/lib/rubygems/security/policy.rb#L177
     trust_dir = opt[:trust_dir]
     time      = Time.now
 
-    signer_digests = digests.find do |algorithm, file_digests|
+    _, signer_digests = digests.find do |algorithm, file_digests|
       file_digests.values.first.name == Gem::Security::DIGEST_NAME
     end
 
-    signer_digests = digests.values.first || {}
+    if @verify_data then
+      raise Gem::Security::Exception, 'no digests provided (probable bug)' if
+        signer_digests.nil? or signer_digests.empty?
+    else
+      signer_digests = {}
+    end
 
     signer = chain.last
 
@@ -195,6 +200,13 @@ class Gem::Security::Policy https://github.com/ruby/ruby/blob/trunk/lib/rubygems/security/policy.rb#L200
 
     check_trust chain, digester, trust_dir if @only_trusted
 
+    signatures.each do |file, _|
+      digest = signer_digests[file]
+
+      raise Gem::Security::Exception, "missing digest for #{file}" unless
+        digest
+    end
+
     signer_digests.each do |file, digest|
       signature = signatures[file]
 
Index: lib/rubygems.rb
===================================================================
--- lib/rubygems.rb	(revision 39125)
+++ lib/rubygems.rb	(revision 39126)
@@ -1,9 +1,9 @@ https://github.com/ruby/ruby/blob/trunk/lib/rubygems.rb#L1
 # -*- ruby -*-
-#
+#--
 # Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
 # All rights reserved.
 # See LICENSE.txt for permissions.
-#
+#++
 
 require 'rbconfig'
 
@@ -109,6 +109,8 @@ require 'rubygems/errors' https://github.com/ruby/ruby/blob/trunk/lib/rubygems.rb#L109
 # Thanks!
 #
 # -The RubyGems Team
+
+
 module Gem
   RUBYGEMS_DIR = File.dirname File.expand_path(__FILE__)
 
Index: test/rubygems/test_gem_package.rb
===================================================================
--- test/rubygems/test_gem_package.rb	(revision 39125)
+++ test/rubygems/test_gem_package.rb	(revision 39126)
@@ -429,10 +429,17 @@ class TestGemPackage < Gem::Package::Tar https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_package.rb#L429
 
       digest = OpenSSL::Digest::SHA1.new
       digest << metadata_gz
-      checksum = "#{digest.name}\t#{digest.hexdigest}\n"
 
-      tar.add_file 'metadata.gz.sum', 0444 do |io|
-        io.write checksum
+      checksums = {
+        'SHA1' => {
+          'metadata.gz' => digest.hexdigest,
+        },
+      }
+
+      tar.add_file 'checksums.yaml.gz', 0444 do |io|
+        Zlib::GzipWriter.wrap io do |gz_io|
+          gz_io.write YAML.dump checksums
+        end
       end
 
       tar.add_file 'data.tar.gz', 0444 do |io|
@@ -502,6 +509,47 @@ class TestGemPackage < Gem::Package::Tar https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_package.rb#L509
 
     refute package.instance_variable_get(:@spec), '@spec must not be loaded'
     assert_empty package.instance_variable_get(:@files), '@files must empty'
+  end
+
+  def test_verify_security_policy_checksum_missing
+    @spec.cert_chain = [PUBLIC_CERT.to_pem]
+    @spec.signing_key = PRIVATE_KEY
+
+    build = Gem::Package.new @gem
+    build.spec = @spec
+    build.setup_signer
+
+    FileUtils.mkdir 'lib'
+    FileUtils.touch 'lib/code.rb'
+
+    open @gem, 'wb' do |gem_io|
+      Gem::Package::TarWriter.new gem_io do |gem|
+        build.add_metadata gem
+        build.add_contents gem
+
+        # write bogus data.tar.gz to foil signature
+        bogus_data = Gem.gzip 'hello'
+        gem.add_file_simple 'data.tar.gz', 0444, bogus_data.length do |io|
+          io.write bogus_data
+        end
+
+        # pre rubygems 2.0 gems do not add checksums
+      end
+    end
+
+    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+    package = Gem::Package.new @gem
+    package.security_policy = Gem::Security::HighSecurity
+
+    e = assert_raises Gem::Security::Exception do
+      package.verify
+    end
+
+    assert_equal 'invalid signature', e.message
+
+    refute package.instance_variable_get(:@spec), '@spec must not be loaded'
+    assert_empty package.instance_variable_get(:@files), '@files must empty'
   end
 
   def test_verify_truncate
Index: test/rubygems/test_gem_security_policy.rb
===================================================================
--- test/rubygems/test_gem_security_policy.rb	(revision 39125)
+++ test/rubygems/test_gem_security_policy.rb	(revision 39126)
@@ -31,6 +31,7 @@ class TestGemSecurityPolicy < Gem::TestC https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_security_policy.rb#L31
     @sha1 = OpenSSL::Digest::SHA1
     @trust_dir = Gem::Security.trust_dir.dir # HACK use the object
 
+    @no        = Gem::Security::NoSecurity
     @almost_no = Gem::Security::AlmostNoSecurity
     @low       = Gem::Security::LowSecurity
     @high      = Gem::Security::HighSecurity
@@ -220,73 +221,108 @@ class TestGemSecurityPolicy < Gem::TestC https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_security_policy.rb#L221
   def test_verify
     Gem::Security.trust_dir.trust_cert PUBLIC_CERT
 
-    assert @almost_no.verify [PUBLIC_CERT]
+    assert @almost_no.verify [PUBLIC_CERT], nil, *dummy_signatures
   end
 
   def test_verify_chain_signatures
     Gem::Security.trust_dir.trust_cert PUBLIC_CERT
 
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data, PRIVATE_KEY) }
-
-    assert @high.verify [PUBLIC_CERT], nil, digest, signature
+    assert @high.verify [PUBLIC_CERT], nil, *dummy_signatures
   end
 
   def test_verify_chain_key
-    assert @almost_no.verify [PUBLIC_CERT], PRIVATE_KEY
+    @almost_no.verify [PUBLIC_CERT], PRIVATE_KEY, *dummy_signatures
   end
 
-  def test_verify_signatures_chain
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data, CHILD_KEY) }
+  def test_verify_no_digests
+    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+    _, signatures = dummy_signatures
+
+    e = assert_raises Gem::Security::Exception do
+      @almost_no.verify [PUBLIC_CERT], nil, {}, signatures
+    end
+
+    assert_equal 'no digests provided (probable bug)', e.message
+  end
+
+  def test_verify_no_digests_no_security
+    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+    _, signatures = dummy_signatures
+
+    e = assert_raises Gem::Security::Exception do
+      @no.verify [PUBLIC_CERT], nil, {}, signatures
+    end
+
+    assert_equal 'missing digest for 0', e.message
+  end
+
+  def test_verify_not_enough_signatures
+    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+    digests, signatures = dummy_signatures
+
+    data = digest 'goodbye'
+
+    signatures[1] = PRIVATE_KEY.sign @sha1.new, data.digest
+
+    e = assert_raises Gem::Security::Exception do
+      @almost_no.verify [PUBLIC_CERT], nil, digests, signatures
+    end
 
+    assert_equal 'missing digest for 1', e.message
+  end
+
+  def test_verify_wrong_digest_type
+    Gem::Security.trust_dir.trust_cert PUBLIC_CERT
+
+    sha512 = OpenSSL::Digest::SHA512
+
+    data = sha512.new
+    data << 'hello'
+
+    digests    = { 'SHA512' => { 0 => data } }
+    signature  = PRIVATE_KEY.sign sha512.new, data.digest
+    signatures = { 0 => signature }
+
+    e = assert_raises Gem::Security::Exception do
+      @almost_no.verify [PUBLIC_CERT], nil, digests, signatures
+    end
+
+    assert_equal 'no digests provided (probable bug)', e.message
+  end
+
+  def test_verify_signatures_chain
     @spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
 
-    assert @chain.verify_signatures @spec, digest, signature
+    assert @chain.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
   end
 
   def test_verify_signatures_data
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data) }
-
     @spec.cert_chain = [PUBLIC_CERT]
 
-    @almost_no.verify_signatures @spec, digest, signature
+    @almost_no.verify_signatures @spec, *dummy_signatures
   end
 
   def test_verify_signatures_root
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data, CHILD_KEY) }
-
     @spec.cert_chain = [PUBLIC_CERT, CHILD_CERT]
 
-    assert @root.verify_signatures @spec, digest, signature
+    assert @root.verify_signatures @spec, *dummy_signatures(CHILD_KEY)
   end
 
   def test_verify_signatures_signer
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data) }
-
     @spec.cert_chain = [PUBLIC_CERT]
 
-    assert @low.verify_signatures @spec, digest, signature
+    assert @low.verify_signatures @spec, *dummy_signatures
   end
 
   def test_verify_signatures_trust
     Gem::Security.trust_dir.trust_cert PUBLIC_CERT
 
-    data = digest 'hello'
-    digest    = { 'SHA1' => { 0 => data } }
-    signature = { 0 => sign(data, PRIVATE_KEY) }
-
     @spec.cert_chain = [PUBLIC_CERT]
 
-    assert @high.verify_signatures @spec, digest, signature
+    assert @high.verify_signatures @spec, *dummy_signatures
   end
 
   def test_verify_signatures
@@ -372,5 +408,14 @@ class TestGemSecurityPolicy < Gem::TestC https://github.com/ruby/ruby/blob/trunk/test/rubygems/test_gem_security_policy.rb#L408
     key.sign @sha1.new, data.digest
   end
 
+  def dummy_signatures key = PRIVATE_KEY
+    data = digest 'hello'
+
+    digests    = { 'SHA1' => { 0 => data } }
+    signatures = { 0 => sign(data, key) }
+
+    return digests, signatures
+  end
+
 end
 

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]