gel 0.3.0 → 0.8.0.pre1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +26 -3
  3. data/RELEASING.md +12 -0
  4. data/exe/gel +4 -2
  5. data/gemlib/gel/stub.rb +20 -0
  6. data/lib/gel/catalog/common.rb +4 -2
  7. data/lib/gel/catalog/compact_index.rb +6 -10
  8. data/lib/gel/catalog/dependency_index.rb +10 -10
  9. data/lib/gel/catalog/legacy_index.rb +4 -6
  10. data/lib/gel/catalog/marshal_hacks.rb +2 -0
  11. data/lib/gel/catalog.rb +33 -52
  12. data/lib/gel/catalog_set.rb +100 -0
  13. data/lib/gel/command/help.rb +13 -2
  14. data/lib/gel/command/lock.rb +3 -3
  15. data/lib/gel/command/open.rb +24 -0
  16. data/lib/gel/command/shell_setup.rb +11 -8
  17. data/lib/gel/command/stub.rb +45 -2
  18. data/lib/gel/command/version.rb +7 -0
  19. data/lib/gel/command.rb +43 -6
  20. data/lib/gel/compatibility/rubygems.rb +10 -197
  21. data/lib/gel/compatibility.rb +2 -2
  22. data/lib/gel/config.rb +41 -7
  23. data/lib/gel/db.rb +93 -83
  24. data/lib/gel/direct_gem.rb +16 -4
  25. data/lib/gel/environment.rb +542 -249
  26. data/lib/gel/error.rb +156 -24
  27. data/lib/gel/gemfile_parser.rb +74 -12
  28. data/lib/gel/gemspec_parser.rb +26 -7
  29. data/lib/gel/git_catalog.rb +15 -3
  30. data/lib/gel/git_depot.rb +62 -28
  31. data/lib/gel/httpool.rb +5 -2
  32. data/lib/gel/installer.rb +61 -23
  33. data/lib/gel/lock_loader.rb +87 -112
  34. data/lib/gel/lock_parser.rb +23 -31
  35. data/lib/gel/locked_store.rb +30 -21
  36. data/lib/gel/multi_store.rb +13 -4
  37. data/lib/gel/null_solver.rb +67 -0
  38. data/lib/gel/package/abortable.rb +18 -0
  39. data/lib/gel/package/installer.rb +124 -49
  40. data/lib/gel/package.rb +21 -4
  41. data/lib/gel/path_catalog.rb +1 -1
  42. data/lib/gel/pinboard.rb +4 -2
  43. data/lib/gel/platform.rb +38 -0
  44. data/lib/gel/pub_grub/package.rb +67 -0
  45. data/lib/gel/pub_grub/preference_strategy.rb +10 -6
  46. data/lib/gel/pub_grub/solver.rb +37 -0
  47. data/lib/gel/pub_grub/source.rb +64 -92
  48. data/lib/gel/resolved_gem_set.rb +234 -0
  49. data/lib/gel/runtime.rb +3 -3
  50. data/lib/gel/set.rb +62 -0
  51. data/lib/gel/stdlib.rb +83 -0
  52. data/lib/gel/store.rb +94 -25
  53. data/lib/gel/store_catalog.rb +2 -2
  54. data/lib/gel/store_gem.rb +54 -6
  55. data/lib/gel/stub_set.rb +32 -2
  56. data/lib/gel/support/cgi_escape.rb +34 -0
  57. data/lib/gel/support/gem_platform.rb +0 -2
  58. data/lib/gel/support/sha512.rb +142 -0
  59. data/lib/gel/support/tar/tar_writer.rb +2 -2
  60. data/lib/gel/tail_file.rb +2 -1
  61. data/lib/gel/util.rb +108 -0
  62. data/lib/gel/vendor/pstore.rb +3 -0
  63. data/lib/gel/vendor/pub_grub.rb +3 -0
  64. data/lib/gel/vendor/ruby_digest.rb +3 -0
  65. data/lib/gel/vendor_catalog.rb +38 -0
  66. data/lib/gel/version.rb +1 -1
  67. data/lib/gel.rb +15 -0
  68. data/man/man1/gel-exec.1 +1 -1
  69. data/man/man1/gel-install.1 +1 -1
  70. data/man/man1/gel.1 +14 -1
  71. data/{lib/gel/compatibility → slib}/bundler/cli.rb +0 -0
  72. data/{lib/gel/compatibility → slib}/bundler/friendly_errors.rb +0 -0
  73. data/{lib/gel/compatibility/rubygems/dependency_installer.rb → slib/bundler/gem_helper.rb} +0 -0
  74. data/slib/bundler/gem_tasks.rb +0 -0
  75. data/{lib/gel/compatibility → slib}/bundler/setup.rb +0 -0
  76. data/{lib/gel/compatibility → slib}/bundler.rb +39 -3
  77. data/{lib/gel/compatibility → slib}/rubygems/command.rb +0 -0
  78. data/slib/rubygems/dependency_installer.rb +12 -0
  79. data/{lib/gel/compatibility → slib}/rubygems/gem_runner.rb +0 -0
  80. data/slib/rubygems/package.rb +6 -0
  81. data/slib/rubygems/package_task.rb +7 -0
  82. data/slib/rubygems/specification.rb +0 -0
  83. data/slib/rubygems/version.rb +0 -0
  84. data/slib/rubygems.rb +297 -0
  85. data/vendor/pstore/LICENSE.txt +22 -0
  86. data/vendor/pstore/lib/pstore.rb +488 -0
  87. data/vendor/pub_grub/LICENSE.txt +21 -0
  88. data/vendor/pub_grub/lib/pub_grub/assignment.rb +20 -0
  89. data/vendor/pub_grub/lib/pub_grub/basic_package_source.rb +183 -0
  90. data/vendor/pub_grub/lib/pub_grub/failure_writer.rb +182 -0
  91. data/vendor/pub_grub/lib/pub_grub/incompatibility.rb +143 -0
  92. data/vendor/pub_grub/lib/pub_grub/package.rb +35 -0
  93. data/vendor/pub_grub/lib/pub_grub/partial_solution.rb +121 -0
  94. data/vendor/pub_grub/lib/pub_grub/rubygems.rb +45 -0
  95. data/vendor/pub_grub/lib/pub_grub/solve_failure.rb +17 -0
  96. data/vendor/pub_grub/lib/pub_grub/static_package_source.rb +53 -0
  97. data/vendor/pub_grub/lib/pub_grub/term.rb +105 -0
  98. data/vendor/pub_grub/lib/pub_grub/version.rb +3 -0
  99. data/vendor/pub_grub/lib/pub_grub/version_constraint.rb +124 -0
  100. data/vendor/pub_grub/lib/pub_grub/version_range.rb +399 -0
  101. data/vendor/pub_grub/lib/pub_grub/version_solver.rb +247 -0
  102. data/vendor/pub_grub/lib/pub_grub/version_union.rb +174 -0
  103. data/vendor/pub_grub/lib/pub_grub.rb +31 -0
  104. data/vendor/ruby-digest/UNLICENSE +24 -0
  105. data/vendor/ruby-digest/lib/ruby_digest.rb +812 -0
  106. metadata +95 -19
@@ -0,0 +1,812 @@
1
+ #--
2
+ # Ruby Digest
3
+ # =============================================================================
4
+ # [![Status](https://travis-ci.org/Solistra/ruby-digest.svg?branch=master)][ci]
5
+ # [ci]: https://travis-ci.org/Solistra/ruby-digest
6
+ #
7
+ # Summary
8
+ # -----------------------------------------------------------------------------
9
+ # Ruby Digest aims to provide pure-Ruby implementations of the digest objects
10
+ # provided by the MRI Ruby 'digest' standard library (originally written as
11
+ # native C extensions). At present, Ruby Digest accurately implements the
12
+ # `MD5`, `SHA1`, and `SHA256` hashing algorithms, the Bubble Babble encoding,
13
+ # and the `HMAC` keyed-hash message authentication code.
14
+ #
15
+ # Ruby Digest has been provided primarily for Ruby environments which do not
16
+ # have access to native extensions for any reason (notable examples include
17
+ # the RPG Maker series and SketchUp Make).
18
+ #
19
+ # Notes
20
+ # -----------------------------------------------------------------------------
21
+ # While Ruby Digest aims to provide a reasonable, pure-Ruby alternative to
22
+ # the MRI Ruby 'digest' standard library, there are a few notable classes
23
+ # missing -- namely the `RMD160`, `SHA384`, and `SHA512` classes.
24
+ #
25
+ # License
26
+ # -----------------------------------------------------------------------------
27
+ # Ruby Digest is free and unencumbered software released into the public
28
+ # domain.
29
+ #
30
+ #++
31
+
32
+ # Gel::Vendor::RubyDigest
33
+ # =============================================================================
34
+ # Provides a pure-Ruby implementation of the MRI 'digest' standard library.
35
+ module Gel::Vendor::RubyDigest
36
+ # The semantic version of {Gel::Vendor::RubyDigest}.
37
+ VERSION = '0.0.1pre'.freeze
38
+
39
+ # A specifically-ordered array of lower-case vowels used to create Bubble
40
+ # Babble-encoded digest hash values.
41
+ VOWELS = %w( a e i o u y ).freeze
42
+
43
+ # A specifically-ordered array of lower-case consonants used to create
44
+ # Bubble Babble-encoded digest hash values.
45
+ CONSONANTS = %w( b c d f g h k l m n p r s t v z x ).freeze
46
+
47
+ # Encodes the given string in Bubble Babble (an encoding designed to be more
48
+ # human-readable than hexadecimal).
49
+ #
50
+ # @param string [String] the string to encode in Bubble Babble
51
+ # @return [String] the Bubble Babble-encoded string
52
+ # @see http://wiki.yak.net/589/Bubble_Babble_Encoding.txt
53
+ def self.bubblebabble(string)
54
+ d = string
55
+ seed = 1
56
+ babble = 'x'
57
+ length = d.length
58
+ rounds = (length / 2) + 1
59
+ 0.upto(rounds - 1) do |i|
60
+ if i + 1 < (rounds || length % 2)
61
+ i0 = (((d[2 * i].ord >> 6) & 3) + seed) % 6
62
+ i1 = (d[2 * i].ord >> 2) & 15
63
+ i2 = ((d[2 * i].ord & 3) + seed / 6) % 6
64
+ babble << "#{VOWELS[i0]}#{CONSONANTS[i1]}#{VOWELS[i2]}"
65
+ if (i + 1 < rounds)
66
+ i0 = (d[2 * i + 1].ord >> 4) & 15
67
+ i1 = d[2 * i + 1].ord & 15
68
+ babble << "#{CONSONANTS[i0]}-#{CONSONANTS[i1]}"
69
+ seed = ((seed * 5) + (d[2 * i].ord * 7) + d[2 * i + 1].ord) % 36
70
+ end
71
+ else
72
+ if length.even?
73
+ babble << "#{VOWELS[seed % 6]}#{CONSONANTS[16]}#{VOWELS[seed / 6]}"
74
+ else
75
+ i0 = (((d[length - 1].ord >> 6) & 3) + seed) % 6
76
+ i1 = (d[length - 1].ord >> 2) & 15
77
+ i2 = (((d[length - 1].ord) & 3) + seed / 6) % 6
78
+ babble << "#{VOWELS[i0]}#{CONSONANTS[i1]}#{VOWELS[i2]}"
79
+ end
80
+ end
81
+ end
82
+ babble << 'x'
83
+ end
84
+
85
+ # Generates a hex-encoded version of the given `string`.
86
+ #
87
+ # @param string [String] the string to hex-encode
88
+ # @return [String] the hex-encoded string
89
+ def self.hexencode(string)
90
+ string.unpack('H*').pack('A*')
91
+ end
92
+
93
+ # Instance
94
+ # ===========================================================================
95
+ # Provides instance methods for a digest implementation object to calculate
96
+ # message digest values.
97
+ module Instance
98
+ # If a string is given, checks whether or not it is equal to the hex-
99
+ # encoded hash value of this digest object. If another digest instance is
100
+ # given, checks whether or not they have the same hexadecimal hash value.
101
+ #
102
+ # @param other [Object] the other object to compare this digest to
103
+ # @return [Boolean] `true` if both objects have matching digest values,
104
+ # `false` otherwise
105
+ def ==(other)
106
+ hexdigest == (other.respond_to?(:hexdigest) ? other.hexdigest : other)
107
+ end
108
+
109
+ # @raise [RuntimeError] if a subclass does not implement this method
110
+ def block_length
111
+ raise RuntimeError, "#{self.class} does not implement block_length()"
112
+ end
113
+
114
+ # @raise [RuntimeError] if a subclass does not implement this method
115
+ def digest_length
116
+ raise RuntimeError, "#{self.class} does not implement digest_length()"
117
+ end
118
+ alias_method :length, :digest_length
119
+ alias_method :size, :length
120
+
121
+ # Returns the resulting base64-encoded hash value of the digest if no
122
+ # string is given, maintaining the digest's state. If a string is given,
123
+ # returns the base64-encoded hash value of the given string, resetting the
124
+ # digest to its initial state.
125
+ #
126
+ # @param str [String, nil] the string to produce a base64-encoded hash
127
+ # value for if given
128
+ # @return [String] the requested base64-encoded digest
129
+ def base64digest(str = nil)
130
+ str.nil? ? clone.base64digest! :
131
+ new.update(str).base64digest!.tap { reset }
132
+ end
133
+
134
+ # Returns the resulting hash value in a base64-encoded form and resets the
135
+ # digest to its initial state.
136
+ #
137
+ # @return [String] the base64-encoded hash value of this digest object
138
+ # before the reset
139
+ def base64digest!
140
+ [finish].pack('m0').tap { reset }
141
+ end
142
+
143
+ # @return [String] the Bubble Babble-encoded hash value of this digest
144
+ # object
145
+ # @see Gel::Vendor::RubyDigest.bubblebabble
146
+ def bubblebabble
147
+ Gel::Vendor::RubyDigest.bubblebabble(digest)
148
+ end
149
+
150
+ # Returns the resulting hash value of the digest if no string is given,
151
+ # maintaining the digest's state. If a string is given, returns the hash
152
+ # value of the given string, resetting the digest to its initial state.
153
+ #
154
+ # @param str [String, nil] the string to produce a hash value for if given
155
+ # @return [String] the requested digest
156
+ def digest(str = nil)
157
+ str.nil? ? clone.digest! : new.update(str).digest!.tap { reset }
158
+ end
159
+
160
+ # Returns the resulting hash value and resets the digest object to its
161
+ # initial state.
162
+ #
163
+ # @return [String] the hash value of this digest object before the reset
164
+ def digest!
165
+ finish.tap { reset }
166
+ end
167
+
168
+ # Updates this digest with the contents of the given `filename` and returns
169
+ # the updated digest object.
170
+ #
171
+ # @param filename [String] the path to a file used to update the digest
172
+ # @return [self] the updated digest instance
173
+ def file(filename)
174
+ File.open(filename, 'rb') do |file|
175
+ buffer = ''
176
+ update(buffer) while file.read(16384, buffer)
177
+ end
178
+ self
179
+ end
180
+
181
+ # @raise [RuntimeError] if a subclass does not implement this method
182
+ def finish
183
+ raise RuntimeError, "#{self.class} does not implement finish()"
184
+ end
185
+ private :finish
186
+
187
+ # Returns the resulting hex-encoded hash value of the digest if no string
188
+ # is given, maintaining the digest's state. If a string is given, returns
189
+ # the hex-encoded hash value of the given string, resetting the digest to
190
+ # its initial state.
191
+ #
192
+ # @param str [String, nil] the string to produce a hex-encoded hash value
193
+ # for if given
194
+ # @return [String] the requested hex-encoded digest
195
+ def hexdigest(str = nil)
196
+ str.nil? ? clone.hexdigest! : new.update(str).hexdigest!.tap { reset }
197
+ end
198
+
199
+ # Returns the resulting hash value in a hex-encoded form and resets the
200
+ # digest object to its initial state.
201
+ #
202
+ # @return [String] the hex-encoded hash value of this digest object before
203
+ # the reset
204
+ def hexdigest!
205
+ finish.unpack('H*').pack('A*').tap { reset }
206
+ end
207
+
208
+ # @return [Gel::Vendor::RubyDigest::Class] a new, initialized copy of this digest object
209
+ def new
210
+ clone.reset
211
+ end
212
+
213
+ # @raise [RuntimeError] if a subclass does not implement this method
214
+ def reset
215
+ raise RuntimeError, "#{self.class} does not implement reset()"
216
+ end
217
+
218
+ # @return [String] the hex-encoded hash value of this digest object
219
+ def to_s
220
+ hexdigest
221
+ end
222
+
223
+ # @raise [RuntimeError] if a subclass does not implement this method
224
+ def update(string)
225
+ raise RuntimeError, "#{self.class} does not implement update()"
226
+ end
227
+ alias_method :<<, :update
228
+ end
229
+ # Class
230
+ # ===========================================================================
231
+ # Stands as a base class for digest implementation classes.
232
+ class Class
233
+ include Instance
234
+
235
+ # The 8-bit field used for bitwise `AND` masking. Defaults to `0xFFFFFFFF`.
236
+ MASK = 0xFFFFFFFF
237
+
238
+ # Hashes the given string, returning the base64-encoded digest.
239
+ #
240
+ # @param string [String] the string to generate a base64-encoded digest for
241
+ # @return [String] the base64-encoded digest of the given string
242
+ def self.base64digest(string, *arguments)
243
+ new(*arguments).update(string).base64digest!
244
+ end
245
+
246
+ # Hashes the given string, returning the Bubble Babble-encoded digest.
247
+ #
248
+ # @param string [String] the string generate a Bubble Babble-encoded digest
249
+ # for
250
+ # @return [String] the Bubble Babble-encoded digest of the given string
251
+ # @see Gel::Vendor::RubyDigest.bubblebabble
252
+ def self.bubblebabble(string)
253
+ Gel::Vendor::RubyDigest.bubblebabble(digest(string))
254
+ end
255
+
256
+ # Hashes the given string, returning the digest.
257
+ #
258
+ # @param string [String] the string to generate a digest for
259
+ # @return [String] the digest of the given string
260
+ def self.digest(string, *arguments)
261
+ new(*arguments).update(string).digest!
262
+ end
263
+
264
+ # Hashes the given string, returning the hex-encoded digest.
265
+ #
266
+ # @param string [String] the string to generate a hex-encoded digest for
267
+ # @return [String] the hex-encoded digest of the given string
268
+ def self.hexdigest(string, *arguments)
269
+ new(*arguments).update(string).hexdigest!
270
+ end
271
+
272
+ # Generates a new digest object representing the hashed contents of the
273
+ # given file.
274
+ #
275
+ # @param filename [String] the path to a file to generate a digest object
276
+ # for
277
+ # @return [Base] a new digest object representing the hashed contents of
278
+ # the given file
279
+ def self.file(filename, *arguments)
280
+ new(*arguments).file(filename)
281
+ end
282
+ end
283
+ # Base
284
+ # ===========================================================================
285
+ # Abstract class providing a common interface to message digest
286
+ # implementation classes.
287
+ class Base < Class
288
+ # Customizes object instantiation to raise a `NotImplementedError` if the
289
+ # object to be initialized is a {Gel::Vendor::RubyDigest::Base} object.
290
+ #
291
+ # @param args [Array<Object>] the arguments to pass to `#initialize`
292
+ # @return [Gel::Vendor::RubyDigest::Base] the new digest object instance
293
+ # @raise [NotImplementedError] if the requested digest object is exactly
294
+ # {Gel::Vendor::RubyDigest::Base}
295
+ def self.new(*args, &block)
296
+ instance = super
297
+ if instance.class == Base
298
+ raise NotImplementedError, "#{self} is an abstract class"
299
+ end
300
+ instance
301
+ end
302
+
303
+ # Defaults to the length of the {#digest} value for this digest object.
304
+ #
305
+ # @return [Fixnum] the length of the hash value of this digest object
306
+ def digest_length
307
+ digest.length
308
+ end
309
+
310
+ # Initializes a new digest object with an empty initial state.
311
+ #
312
+ # @return [self] the new digest object
313
+ def initialize
314
+ @buffer = ''
315
+ end
316
+
317
+ # Customizes duplication of this digest object, properly setting the buffer
318
+ # of the duplicate to a copy of the source object's buffer.
319
+ #
320
+ # @note This method exists so that duplicate digest objects do not refer to
321
+ # the same base string buffer as the source digest object.
322
+ #
323
+ # @param source [Gel::Vendor::RubyDigest::Base] the digest object being duplicated
324
+ # @return [Gel::Vendor::RubyDigest::Base] the duplicate digest object
325
+ def initialize_copy(source)
326
+ super
327
+ @buffer = source.instance_eval { @buffer.clone }
328
+ end
329
+
330
+ # @return [String] a human-readable representation of this digest object
331
+ def inspect
332
+ "#<#{self.class.name}: #{hexdigest}>"
333
+ end
334
+
335
+ # Updates the digest with the given `string`, returning the updated digest
336
+ # instance.
337
+ #
338
+ # @param string [String] the string to update the digest with
339
+ # @return [self] the updated digest instance
340
+ # @raise [TypeError] if the given `string` is not a string
341
+ def update(string)
342
+ unless string.kind_of?(String)
343
+ raise TypeError, "can't convert #{string.class.inspect} into String"
344
+ end
345
+ tap { @buffer << string }
346
+ end
347
+ alias_method :<<, :update
348
+
349
+ # Resets the digest object to its initial state and returns the digest
350
+ # instance.
351
+ #
352
+ # @return [self] the reset digest instance
353
+ def reset
354
+ tap { @buffer.clear }
355
+ end
356
+ end
357
+ # MD5
358
+ # ===========================================================================
359
+ # Provides a pure-Ruby implementation of an MD5 digest object.
360
+ class MD5 < Base
361
+ # The initial constant values for the 32-bit constant words A, B, C, and D,
362
+ # respectively.
363
+ @@words = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476]
364
+
365
+ # Generate and store the initial constant values used by the MD5 algorithm
366
+ # to mutate data -- all of this information is directly used in the
367
+ # `.hexdigest` method of this class.
368
+ @@initial_values = lambda do |f, t, k, s, n4|
369
+ 1.upto(64) do |i|
370
+ t[i] = (Math.sin(i).abs * 0x100000000).truncate
371
+ n4[i] = Array.new(4) { |j| ((65 - i) + j) % 4 }
372
+ case i
373
+ when 1..16
374
+ f[i] = lambda { |x, y, z| (x & y) | ((~x) & z) }
375
+ k[i] = i - 1
376
+ s[i] = [7, 12, 17, 22][(i - 1) % 4]
377
+ when 17..32
378
+ f[i] = lambda { |x, y, z| (x & z) | (y & (~z)) }
379
+ k[i] = (1 + (i - 17) * 5) % 16
380
+ s[i] = [5, 9, 14, 20][(i - 1) % 4]
381
+ when 33..48
382
+ f[i] = lambda { |x, y, z| x ^ y ^ z }
383
+ k[i] = (5 + (i - 33) * 3) % 16
384
+ s[i] = [4, 11, 16, 23][(i - 1) % 4]
385
+ when 49..64
386
+ f[i] = lambda { |x, y, z| y ^ (x | (~z)) }
387
+ k[i] = ((i - 49) * 7) % 16
388
+ s[i] = [6, 10, 15, 21][(i - 1) % 4]
389
+ end
390
+ end
391
+ [f, t, k, s, n4]
392
+ end.call(*Array.new(5) { [] })
393
+
394
+ # @return [64] MD5 digests always have a block length of 64 bytes
395
+ def block_length
396
+ 64
397
+ end
398
+
399
+ # @return [16] MD5 digests always have a length of 16 bytes
400
+ def digest_length
401
+ 16
402
+ end
403
+ alias_method :length, :digest_length
404
+ alias_method :size, :length
405
+
406
+ # Hashes the buffer of this MD5 digest object, returning the computed hash
407
+ # value.
408
+ #
409
+ # @return [String] the hash value of this MD5 digest object
410
+ def finish
411
+ words = @@words.dup
412
+ f, t, k, s, n4 = *@@initial_values
413
+ generate_split_buffer(@buffer) do |chunk|
414
+ words2 = words.dup
415
+ 1.upto(64) do |r|
416
+ words[n4[r][0]] = MASK & (words[n4[r][0]] +
417
+ f[r].call(*n4[r][1..3].map { |e| words[e] }) + chunk[k[r]] + t[r])
418
+ words[n4[r][0]] = rotate(words[n4[r][0]], s[r])
419
+ words[n4[r][0]] = MASK & (words[n4[r][0]] + words[n4[r][1]])
420
+ end
421
+ words.map!.with_index { |word, index| MASK & (word + words2[index]) }
422
+ end
423
+ words.reduce('') do |digest, word|
424
+ digest << [MASK & word].pack('V')
425
+ end.tap { reset }
426
+ end
427
+ private :finish
428
+
429
+ # Generates a split buffer of string values used to perform the main loop
430
+ # of the hashing algorithm.
431
+ #
432
+ # @param string [String] the base string to generate a split buffer from
433
+ # @yieldreturn [String] each chunk of the split buffer
434
+ # @return [Array<String>] the split buffer
435
+ def generate_split_buffer(string)
436
+ size = string.size * 8
437
+ buffer = string + ['10000000'].pack('B8')
438
+ buffer << [0].pack('C') while buffer.size % 64 != 56
439
+ buffer << [MASK & size].pack('V') + [size >> 32].pack('V')
440
+ split = [].tap do |a|
441
+ (buffer.size / 64).times { |i| a[i] = buffer[i*64,64].unpack('V16') }
442
+ end
443
+ block_given? ? split.each { |chunk| yield chunk } : split
444
+ end
445
+ private :generate_split_buffer
446
+
447
+ # Binary left-rotates the given `value` by the given number of `spaces`.
448
+ #
449
+ # @param value [Fixnum] the value to binary left-rotate
450
+ # @param spaces [Fixnum] the number of spaces to shift to the left
451
+ # @return [Fixnum] the left-rotated value
452
+ def rotate(value, spaces)
453
+ value << spaces | value >> (32 - spaces)
454
+ end
455
+ private :rotate
456
+ end
457
+ # SHA1
458
+ # ===========================================================================
459
+ # Provides a pure-Ruby implementation of an SHA1 digest object.
460
+ class SHA1 < Base
461
+ # The initial constant values for the 32-bit constant words A, B, C, D, and
462
+ # E, respectively.
463
+ @@words = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]
464
+
465
+ # @return [64] SHA1 digests always have a block length of 64 bytes
466
+ def block_length
467
+ 64
468
+ end
469
+
470
+ # @return [20] SHA1 digests always have a length of 20 bytes
471
+ def digest_length
472
+ 20
473
+ end
474
+ alias_method :length, :digest_length
475
+ alias_method :size, :length
476
+
477
+ # Hashes the buffer of this SHA1 digest object, returning the computed hash
478
+ # value.
479
+ #
480
+ # @return [String] the hash value of this SHA1 digest object
481
+ def finish
482
+ words = @@words.dup
483
+ generate_split_buffer(@buffer) do |chunk|
484
+ w = []
485
+ a, b, c, d, e = *words
486
+ chunk.each_slice(4) do |a, b, c, d|
487
+ w << (((a << 8 | b) << 8 | c) << 8 | d)
488
+ end
489
+ (16..79).map do |i|
490
+ w[i] = MASK & rotate((w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16]), 1)
491
+ end
492
+ 0.upto(79) do |i|
493
+ f, k = case i
494
+ when 0..19 then [((b & c) | (~b & d)), 0x5A827999]
495
+ when 20..39 then [(b ^ c ^ d), 0x6ED9EBA1]
496
+ when 40..59 then [((b & c) | (b & d) | (c & d)), 0x8F1BBCDC]
497
+ when 60..79 then [(b ^ c ^ d), 0xCA62C1D6]
498
+ end
499
+ t = MASK & (MASK & rotate(a, 5) + f + e + k + w[i])
500
+ a, b, c, d, e = t, a, MASK & rotate(b, 30), c, d
501
+ end
502
+ mutated = [a, b, c, d, e]
503
+ words.map!.with_index { |word, index| MASK & (word + mutated[index]) }
504
+ end
505
+ words.reduce('') do |digest, word|
506
+ digest << [word].pack('N')
507
+ end.tap { reset }
508
+ end
509
+ private :finish
510
+
511
+ # Generates a split buffer of integer values used to perform the main loop
512
+ # of the hashing algorithm.
513
+ #
514
+ # @param string [String] the base string to generate a split buffer from
515
+ # @yieldreturn [Array<Fixnum>] each 64-element chunk of the split buffer
516
+ # @return [Array<Fixnum>] the split buffer
517
+ def generate_split_buffer(string)
518
+ size = string.size * 8
519
+ buffer = string + ['10000000'].pack('B8')
520
+ buffer << [0].pack('C') while buffer.size % 64 != 56
521
+ buffer << [size].pack('Q').reverse
522
+ buffer = buffer.unpack('C*')
523
+ block_given? ? buffer.each_slice(64) { |chunk| yield chunk } : buffer
524
+ end
525
+ private :generate_split_buffer
526
+
527
+ # Binary left-rotates the given `value` by the given number of `spaces`.
528
+ #
529
+ # @param value [Fixnum] the value to binary left-rotate
530
+ # @param spaces [Fixnum] the number of spaces to shift to the left
531
+ # @return [Fixnum] the left-rotated value
532
+ def rotate(value, spaces)
533
+ value << spaces | value >> (32 - spaces)
534
+ end
535
+ private :rotate
536
+ end
537
+ # SHA256
538
+ # ===========================================================================
539
+ # Provides a pure-Ruby implementation of an SHA256 digest object.
540
+ class SHA256 < Base
541
+ # The initial constant values for the 32-bit constant words A, B, C, D, E,
542
+ # F, G, and H, respectively.
543
+ @@words = [
544
+ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A,
545
+ 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19
546
+ ]
547
+
548
+ # The constant values used for word mutation each round.
549
+ #
550
+ # @note There are 64 rounds per mutation for the SHA256 algorithm.
551
+ @@rounds = [
552
+ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1,
553
+ 0x923F82A4, 0xAB1C5ED5, 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3,
554
+ 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, 0xE49B69C1, 0xEFBE4786,
555
+ 0x0FC19DC6, 0x240CA1CC, 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA,
556
+ 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, 0xC6E00BF3, 0xD5A79147,
557
+ 0x06CA6351, 0x14292967, 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13,
558
+ 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, 0xA2BFE8A1, 0xA81A664B,
559
+ 0xC24B8B70, 0xC76C51A3, 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070,
560
+ 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, 0x391C0CB3, 0x4ED8AA4A,
561
+ 0x5B9CCA4F, 0x682E6FF3, 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208,
562
+ 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2
563
+ ]
564
+
565
+ # @return [64] SHA256 digests always have a block length of 64 bytes
566
+ def block_length
567
+ 64
568
+ end
569
+
570
+ # @return [32] SHA256 digests always have a length of 32 bytes
571
+ def digest_length
572
+ 32
573
+ end
574
+ alias_method :length, :digest_length
575
+ alias_method :size, :length
576
+
577
+ # Hashes the buffer of this SHA256 digest object, returning the computed
578
+ # hash value.
579
+ #
580
+ # @return [String] the hash value of this SHA256 digest object
581
+ def finish
582
+ words = @@words.dup
583
+ generate_split_buffer(@buffer) do |chunk|
584
+ w = []
585
+ a, b, c, d, e, f, g, h, = *words
586
+ chunk.each_slice(4) do |a, b, c, d|
587
+ w << (((a << 8 | b) << 8 | c) << 8 | d)
588
+ end
589
+ 16.upto(63) do |i|
590
+ s0 = rotate(w[i - 15], 7) ^ rotate(w[i - 15], 18) ^ (w[i - 15] >> 3)
591
+ s1 = rotate(w[i - 2], 17) ^ rotate(w[i - 2], 19) ^ (w[i - 2] >> 10)
592
+ w[i] = MASK & (w[i - 16] + s0 + w[i - 7] + s1)
593
+ end
594
+ 0.upto(63) do |i|
595
+ s0 = rotate(a, 2) ^ rotate(a, 13) ^ rotate(a, 22)
596
+ maj = (a & b) ^ (a & c) ^ (b & c)
597
+ t2 = MASK & (s0 + maj)
598
+ s1 = rotate(e, 6) ^ rotate(e, 11) ^ rotate(e, 25)
599
+ ch = (e & f) ^ ((~e) & g)
600
+ t1 = MASK & (h + s1 + ch + @@rounds[i] + w[i])
601
+ tmp1 = MASK & (t1 + t2)
602
+ tmp2 = MASK & (d + t1)
603
+ a, b, c, d, e, f, g, h = tmp1, a, b, c, tmp2, e, f, g
604
+ end
605
+ mutated = [a, b, c, d, e, f, g, h]
606
+ words.map!.with_index { |word, index| MASK & (word + mutated[index]) }
607
+ end
608
+ words.reduce('') do |digest, word|
609
+ digest << [word].pack('N')
610
+ end.tap { reset }
611
+ end
612
+ private :finish
613
+
614
+ # Generates a split buffer of integer values used to perform the main loop
615
+ # of the hashing algorithm.
616
+ #
617
+ # @param string [String] the base string to generate a split buffer from
618
+ # @yieldreturn [Array<Fixnum>] each 64-element chunk of the split buffer
619
+ # @return [Array<Fixnum>] the split buffer
620
+ def generate_split_buffer(string)
621
+ size = string.size * 8
622
+ buffer = string + ['10000000'].pack('B8')
623
+ buffer << [0].pack('C') while buffer.size % 64 != 56
624
+ buffer << [size].pack('Q').reverse
625
+ buffer = buffer.unpack('C*')
626
+ block_given? ? buffer.each_slice(64) { |chunk| yield chunk } : buffer
627
+ end
628
+ private :generate_split_buffer
629
+
630
+ # Binary right-rotates the given `value` by the given number of `spaces`.
631
+ #
632
+ # @param value [Fixnum] the value to binary right-rotate
633
+ # @param spaces [Fixnum] the number of spaces to shift to the right
634
+ # @return [Fixnum] the right-rotated value
635
+ def rotate(value, spaces)
636
+ value >> spaces | value << (32 - spaces)
637
+ end
638
+ private :rotate
639
+ end
640
+ # SHA2
641
+ # ===========================================================================
642
+ # Provides a wrapper class for the SHA2 family of digest objects.
643
+ class SHA2 < Class
644
+ # Delegates calls to `#block_length` to the underlying SHA2 digest object.
645
+ #
646
+ # @return [Fixnum] the block length of the digest object
647
+ def block_length
648
+ @sha2.block_length
649
+ end
650
+
651
+ # Delegates calls to `#digest_length` to the underlying SHA2 digest object.
652
+ #
653
+ # @return [Fixnum] the digest length of the digest object
654
+ def digest_length
655
+ @sha2.digest_length
656
+ end
657
+ alias_method :length, :digest_length
658
+ alias_method :size, :length
659
+
660
+ # Delegates calls to `#finish` to the underlying SHA2 digest object's
661
+ # `#digest!` instance method.
662
+ #
663
+ # @return [String] the resulting hash value of the digest object
664
+ def finish
665
+ @sha2.digest!
666
+ end
667
+ private :finish
668
+
669
+ # Initializes a new {SHA2} digest object of the given `bit_length` with an
670
+ # empty initial state.
671
+ #
672
+ # @param bit_length [Fixnum] the desired SHA2 digest length in bits
673
+ # @return [self] the new {SHA2} instance
674
+ # @raise [ArgumentError] if an invalid digest bit length is requested
675
+ def initialize(bit_length = 256)
676
+ case bit_length
677
+ when 256 then @sha2 = Gel::Vendor::RubyDigest::SHA256.new
678
+ else
679
+ raise ArgumentError, "unsupported bit length: #{bit_length.inspect}"
680
+ end
681
+ @sha2.send(:initialize)
682
+ @bit_length = bit_length
683
+ end
684
+
685
+ # Customizes duplication of this {SHA2} object, properly setting the digest
686
+ # object of the duplicate to a copy of the source object's digest object.
687
+ #
688
+ # @note This method exists so that duplicate {SHA2} objects do not refer to
689
+ # the same base digest object as the source digest object.
690
+ #
691
+ # @param source [Gel::Vendor::RubyDigest::SHA2] the {SHA2} object being duplicated
692
+ # @return [Gel::Vendor::RubyDigest::SHA2] the duplicate {SHA2} object
693
+ def initialize_copy(source)
694
+ super
695
+ @sha2 = source.instance_eval { @sha2.clone }
696
+ end
697
+
698
+ # @return [String] a human-readable representation of this {SHA2} instance
699
+ def inspect
700
+ "#<#{self.class.name}:#{@bit_length} #{hexdigest}>"
701
+ end
702
+
703
+ # Delegates calls to `#reset` to the underlying SHA2 digest object.
704
+ #
705
+ # @return [self] the reset {SHA2} instance
706
+ def reset
707
+ tap { @sha2.reset }
708
+ end
709
+
710
+ # Delegates calls to `#update` to the underlying SHA2 digest object.
711
+ #
712
+ # @param string [String] the string to update the digest with
713
+ # @return [self] the updated {SHA2} instance
714
+ def update(string)
715
+ tap { @sha2.update(string) }
716
+ end
717
+ alias_method :<<, :update
718
+ end
719
+ # HMAC
720
+ # ===========================================================================
721
+ # Provides a keyed-hash message authentication code object.
722
+ class HMAC < Class
723
+ # Delegates calls to `#block_length` to the underlying digest object.
724
+ #
725
+ # @return [Fixnum] the block length of the digest object
726
+ def block_length
727
+ @md.block_length
728
+ end
729
+
730
+ # Delegates calls to `#digest_length` to the underlying digest object.
731
+ #
732
+ # @return [Fixnum] the digest length of the digest object
733
+ def digest_length
734
+ @md.digest_length
735
+ end
736
+ alias_method :length, :digest_length
737
+ alias_method :size, :length
738
+
739
+ # Delegates calls to `#finish` to the underlying digest object, properly
740
+ # managing the digest object's buffer for `HMAC`.
741
+ #
742
+ # @return [String] the resulting hash value of the digest object
743
+ def finish
744
+ original = @md.digest!
745
+ @md.update(@opad).update(original).digest!
746
+ end
747
+ private :finish
748
+
749
+ # Initializes a new keyed-hash message authentication code ({HMAC}) object
750
+ # with the given key and digest object.
751
+ #
752
+ # @note {HMAC} objects are significantly more secure than an individual
753
+ # hashing algorithm on its own.
754
+ #
755
+ # @param key [String] the key for this {HMAC} object
756
+ # @param digest_class [Gel::Vendor::RubyDigest::Base] the digest object for this {HMAC}
757
+ def initialize(key, digest_class)
758
+ @md = digest_class.new
759
+
760
+ length = @md.block_length
761
+ key = @md.digest(key) if key.bytesize > length
762
+ ipad = Array.new(length, 0x36) # Inner HMAC padding.
763
+ opad = Array.new(length, 0x5C) # Outer HMAC padding.
764
+
765
+ key.bytes.each_with_index do |character, index|
766
+ ipad[index] ^= character
767
+ opad[index] ^= character
768
+ end
769
+
770
+ @key = key.freeze
771
+ @ipad = ipad.pack('C*').freeze
772
+ @opad = opad.pack('C*').freeze
773
+ @md.update(@ipad)
774
+ end
775
+
776
+ # Customizes duplication of this {HMAC} object, properly setting the digest
777
+ # object of the duplicate to a copy of the source object's digest object.
778
+ #
779
+ # @note This method exists so that duplicate {HMAC} objects do not refer to
780
+ # the same base digest object as the source digest object.
781
+ #
782
+ # @param source [Gel::Vendor::RubyDigest::HMAC] the {HMAC} object being duplicated
783
+ # @return [Gel::Vendor::RubyDigest::HMAC] the duplicate {HMAC} object
784
+ def initialize_copy(source)
785
+ super
786
+ @md = source.instance_eval { @md.clone }
787
+ end
788
+
789
+ # @return [String] a human-readable representation of this {HMAC} instance
790
+ def inspect
791
+ digest = @md.inspect.sub(/^\#<(.*)>$/) { $1 }
792
+ "#<#{self.class.name}: key=#{@key.inspect} digest=#{digest}>"
793
+ end
794
+
795
+ # Delegates calls to `#reset` to the underlying digest object, properly
796
+ # updating its contents with the ipad of this {HMAC} object.
797
+ #
798
+ # @return [self] the reset {HMAC} instance
799
+ def reset
800
+ tap { @md.reset.update(@ipad) }
801
+ end
802
+
803
+ # Delegates calls to `#update` to the underlying digest object.
804
+ #
805
+ # @param string [String] the string to update the digest with
806
+ # @return [self] the updated {HMAC} instance
807
+ def update(string)
808
+ tap { @md.update(string) }
809
+ end
810
+ alias_method :<<, :update
811
+ end
812
+ end