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

ruby-changes:69414

From: Jean <ko1@a...>
Date: Mon, 25 Oct 2021 20:30:26 +0900 (JST)
Subject: [ruby-changes:69414] 5af3f7f357 (master): [rubygems/rubygems] Vendor a pure ruby implementation of SHA1

https://git.ruby-lang.org/ruby.git/commit/?id=5af3f7f357

From 5af3f7f3574c16ec76fb44b21beec17a74f4417a Mon Sep 17 00:00:00 2001
From: Jean Boussier <jean.boussier@g...>
Date: Mon, 18 Oct 2021 10:12:39 +0200
Subject: [rubygems/rubygems] Vendor a pure ruby implementation of SHA1

This allows `Source::Git` to no longer load the `digest` gem as it is causing
issues on Ruby 3.1.

https://github.com/rubygems/rubygems/pull/4989/commits/c19a9f2ff7
---
 lib/bundler.rb                      |  1 +
 lib/bundler/digest.rb               | 71 +++++++++++++++++++++++++++++++++++++
 lib/bundler/gem_helper.rb           |  2 +-
 lib/bundler/source/git.rb           |  4 ++-
 spec/bundler/bundler/digest_spec.rb | 17 +++++++++
 spec/bundler/runtime/setup_spec.rb  | 35 ++++++++++++++++++
 6 files changed, 128 insertions(+), 2 deletions(-)
 create mode 100644 lib/bundler/digest.rb
 create mode 100644 spec/bundler/bundler/digest_spec.rb

diff --git a/lib/bundler.rb b/lib/bundler.rb
index f2d874bdf3f..81dfd05d26d 100644
--- a/lib/bundler.rb
+++ b/lib/bundler.rb
@@ -43,6 +43,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler.rb#L43
   autoload :Dependency,             File.expand_path("bundler/dependency", __dir__)
   autoload :DepProxy,               File.expand_path("bundler/dep_proxy", __dir__)
   autoload :Deprecate,              File.expand_path("bundler/deprecate", __dir__)
+  autoload :Digest,                 File.expand_path("bundler/digest", __dir__)
   autoload :Dsl,                    File.expand_path("bundler/dsl", __dir__)
   autoload :EndpointSpecification,  File.expand_path("bundler/endpoint_specification", __dir__)
   autoload :Env,                    File.expand_path("bundler/env", __dir__)
diff --git a/lib/bundler/digest.rb b/lib/bundler/digest.rb
new file mode 100644
index 00000000000..d560b82439a
--- /dev/null
+++ b/lib/bundler/digest.rb
@@ -0,0 +1,71 @@ https://github.com/ruby/ruby/blob/trunk/lib/bundler/digest.rb#L1
+# frozen_string_literal: true
+
+# This code was extracted from https://github.com/Solistra/ruby-digest which is under public domain
+module Bundler
+  module Digest
+    # The initial constant values for the 32-bit constant words A, B, C, D, and
+    # E, respectively.
+    SHA1_WORDS = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0].freeze
+
+    # The 8-bit field used for bitwise `AND` masking. Defaults to `0xFFFFFFFF`.
+    SHA1_MASK = 0xFFFFFFFF
+
+    class << self
+      def sha1(string)
+        unless string.is_a?(String)
+          raise TypeError, "can't convert #{string.class.inspect} into String"
+        end
+
+        buffer = string.b
+
+        words = SHA1_WORDS.dup
+        generate_split_buffer(buffer) do |chunk|
+          w = []
+          chunk.each_slice(4) do |a, b, c, d|
+            w << (((a << 8 | b) << 8 | c) << 8 | d)
+          end
+          a, b, c, d, e = *words
+          (16..79).each do |i|
+            w[i] = SHA1_MASK & rotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1)
+          end
+          0.upto(79) do |i|
+            case i
+            when  0..19
+              f = ((b & c) | (~b & d))
+              k = 0x5A827999
+            when 20..39
+              f = (b ^ c ^ d)
+              k = 0x6ED9EBA1
+            when 40..59
+              f = ((b & c) | (b & d) | (c & d))
+              k = 0x8F1BBCDC
+            when 60..79
+              f = (b ^ c ^ d)
+              k = 0xCA62C1D6
+            end
+            t = SHA1_MASK & (SHA1_MASK & rotate(a, 5) + f + e + k + w[i])
+            a, b, c, d, e = t, a, SHA1_MASK & rotate(b, 30), c, d # rubocop:disable Style/ParallelAssignment
+          end
+          mutated = [a, b, c, d, e]
+          words.map!.with_index {|word, index| SHA1_MASK & (word + mutated[index]) }
+        end
+
+        words.pack("N*").unpack("H*").first
+      end
+
+      private
+
+      def generate_split_buffer(string, &block)
+        size   = string.bytesize * 8
+        buffer = string.bytes << 128
+        buffer << 0 while buffer.size % 64 != 56
+        [size].pack("Q").bytes.reverse_each {|b| buffer << b }
+        buffer.each_slice(64, &block)
+      end
+
+      def rotate(value, spaces)
+        value << spaces | value >> (32 - spaces)
+      end
+    end
+  end
+end
diff --git a/lib/bundler/gem_helper.rb b/lib/bundler/gem_helper.rb
index 9af85015254..60b9e578878 100644
--- a/lib/bundler/gem_helper.rb
+++ b/lib/bundler/gem_helper.rb
@@ -107,7 +107,7 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/gem_helper.rb#L107
       SharedHelpers.filesystem_access(File.join(base, "checksums")) {|p| FileUtils.mkdir_p(p) }
       file_name = "#{File.basename(built_gem_path)}.sha512"
       require "digest/sha2"
-      checksum = Digest::SHA512.new.hexdigest(built_gem_path.to_s)
+      checksum = ::Digest::SHA512.new.hexdigest(built_gem_path.to_s)
       target = File.join(base, "checksums", file_name)
       File.write(target, checksum)
       Bundler.ui.confirm "#{name} #{version} checksum written to checksums/#{file_name}."
diff --git a/lib/bundler/source/git.rb b/lib/bundler/source/git.rb
index 679fb225745..a41a2f23e96 100644
--- a/lib/bundler/source/git.rb
+++ b/lib/bundler/source/git.rb
@@ -307,7 +307,9 @@ module Bundler https://github.com/ruby/ruby/blob/trunk/lib/bundler/source/git.rb#L307
           # If there is no URI scheme, assume it is an ssh/git URI
           input = uri
         end
-        SharedHelpers.digest(:SHA1).hexdigest(input)
+        # We use SHA1 here for historical reason and to preserve backward compatibility.
+        # But a transition to a simpler mangling algorithm would be welcome.
+        Bundler::Digest.sha1(input)
       end
 
       def cached_revision
diff --git a/spec/bundler/bundler/digest_spec.rb b/spec/bundler/bundler/digest_spec.rb
new file mode 100644
index 00000000000..d6bb043fd00
--- /dev/null
+++ b/spec/bundler/bundler/digest_spec.rb
@@ -0,0 +1,17 @@ https://github.com/ruby/ruby/blob/trunk/spec/bundler/bundler/digest_spec.rb#L1
+# frozen_string_literal: true
+
+require "digest"
+require "bundler/digest"
+
+RSpec.describe Bundler::Digest do
+  context "SHA1" do
+    subject { Bundler::Digest }
+    let(:stdlib) { ::Digest::SHA1 }
+
+    it "is compatible with stdlib" do
+      ["foo", "skfjsdlkfjsdf", "3924m", "ldskfj"].each do |payload|
+        expect(subject.sha1(payload)).to be == stdlib.hexdigest(payload)
+      end
+    end
+  end
+end
diff --git a/spec/bundler/runtime/setup_spec.rb b/spec/bundler/runtime/setup_spec.rb
index 42bbacea0e3..367ef9c7115 100644
--- a/spec/bundler/runtime/setup_spec.rb
+++ b/spec/bundler/runtime/setup_spec.rb
@@ -1228,6 +1228,41 @@ end https://github.com/ruby/ruby/blob/trunk/spec/bundler/runtime/setup_spec.rb#L1228
   end
 
   describe "with gemified standard libraries" do
+    it "does not load Digest", :ruby_repo do
+      skip "Only for Ruby 3.0+" unless RUBY_VERSION >= "3.0"
+
+      build_git "bar", :gemspec => false do |s|
+        s.write "lib/bar/version.rb", %(BAR_VERSION = '1.0')
+        s.write "bar.gemspec", <<-G
+          require_relative 'lib/bar/version'
+
+          Gem::Specification.new do |s|
+            s.name        = 'bar'
+            s.version     = BAR_VERSION
+            s.summary     = 'Bar'
+            s.files       = Dir["lib/**/*.rb"]
+            s.author      = 'no one'
+
+            s.add_runtime_dependency 'digest'
+          end
+        G
+      end
+
+      gemfile <<-G
+        source "#{file_uri_for(gem_repo1)}"
+        gem "bar", :git => "#{lib_path("bar-1.0")}"
+      G
+
+      bundle :install
+
+      ruby <<-RUBY
+        require '#{entrypoint}/setup'
+        puts defined?(::Digest) ? "Digest defined" : "Digest undefined"
+        require 'digest'
+      RUBY
+      expect(out).to eq("Digest undefined")
+    end
+
     it "does not load Psych" do
       gemfile "source \"#{file_uri_for(gem_repo1)}\""
       ruby <<-RUBY
-- 
cgit v1.2.1


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

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