3 # Creates a new Digest Class of +name+ using the C functions
4 # +init_function+, +update_function+ and +finish_function+. The C
5 # algorithm's state is allocated from a C struct of size +struct_size+.
6 # The algorithm's block length is set to +block_length+ and digest output
7 # length to +digest_length+.
9 # Before calling, the C implementation of the algorithm needs to be loaded.
11 # The C functions are attached using the following signatures:
12 # [+init_function+] [:pointer], :void
13 # [+update_function+] [:pointer, :string, :int], :void
14 # [+finish_function+] [:pointer, :string], :void
16 # See digest/md5.rb for an example of usage.
17 def self.create(name, init_function, update_function, finish_function,
18 struct_size, block_length, digest_length)
19 klass = ::Class.new Digest::Instance
20 Digest.const_set name, klass
22 context = ::Class.new FFI::Struct
23 # HACK FFI doesn't understand C arrays
24 context.instance_variable_set :@size, struct_size
25 klass.const_set :Context, context
27 klass.attach_function init_function, :digest_init, [:pointer], :void
28 klass.attach_function update_function, :digest_update,
29 [:pointer, :string, :int], :void
30 klass.attach_function finish_function, :digest_finish,
31 [:pointer, :string], :void
33 klass.const_set :BLOCK_LENGTH, block_length
34 klass.const_set :DIGEST_LENGTH, digest_length
36 klass.extend Digest::Class
41 # Generates a hex-encoded version of a given +string+
42 def self.hexencode(string)
43 hex = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f']
45 result = ' '*(string.length * 2)
47 string.split('').to_a.each_with_index do |byte,i|
49 result[i + i] = hex[byte >> 4];
50 result[i + i + 1] = hex[byte & 0x0f];
56 # This module provides instance methods for a digest implementation
57 # object to calculate message digest values.
65 def initialize_copy(other)
66 @context = @context.dup
70 # digest_obj.update(string) -> digest_obj
71 # digest_obj << string -> digest_obj
73 # Updates the digest using a given +string+ and returns self.
75 # The update method and the left-shift operator are overridden by
76 # each implementation subclass. (One should be an alias for the
79 self.class.digest_update @context.pointer, string, string.length
85 # digest_obj.instance_eval { finish } -> digest_obj
87 # Finishes the digest and returns the resulting hash value.
89 # This method is overridden by each implementation subclass and often
90 # made private, because some of those subclasses may leave internal
91 # data uninitialized. Do not call this method from outside. Use
92 # #digest! instead, which ensures that internal data be reset for
95 value = ' ' * digest_length
96 self.class.digest_finish @context.pointer, value
103 # digest_obj.reset -> digest_obj
105 # Resets the digest to the initial state and returns self.
107 # This method is overridden by each implementation subclass.
109 @context.free if @context
110 @context = self.class::Context.new
111 self.class.digest_init @context.pointer
115 # digest_obj.new -> another_digest_obj
117 # Returns a new, initialized copy of the digest object. Equivalent
118 # to digest_obj.clone.reset.
124 # digest_obj.digest -> string
125 # digest_obj.digest(string) -> string
127 # If none is given, returns the resulting hash value of the digest,
128 # keeping the digest's state.
130 # If a +string+ is given, returns the hash value for the given
131 # +string+, resetting the digest to the initial state before and
133 def digest(string=nil)
149 # digest_obj.digest! -> string
151 # Returns the resulting hash value and resets the digest to the
160 # digest_obj.hexdigest -> string
161 # digest_obj.hexdigest(string) -> string
163 # If none is given, returns the resulting hash value of the digest in
164 # a hex-encoded form, keeping the digest's state.
166 # If a +string+ is given, returns the hash value for the given
167 # +string+ in a hex-encoded form, resetting the digest to the initial
168 # state before and after the process.
169 def hexdigest(data=nil)
170 Digest.hexencode(digest(data))
174 # digest_obj.hexdigest! -> string
176 # Returns the resulting hash value and resets the digest to the
185 # digest_obj.to_s -> string
187 # Returns digest_obj.hexdigest.
193 # digest_obj.inspect -> string
195 # Creates a printable version of the digest object.
197 "#<#{self.class}: #{self.hexdigest}>"
201 # digest_obj == another_digest_obj -> boolean
202 # digest_obj == string -> boolean
204 # If a string is given, checks whether it is equal to the hex-encoded
205 # digest value of the digest object. If another digest instance is
206 # given, checks whether they have the same digest value. Otherwise
209 return hexdigest == other.hexdigest if other.is_a? Digest::Instance
214 # digest_obj.digest_length -> integer
216 # Returns the length of the hash value of the digest.
218 # This method should be overridden by each implementation subclass.
219 # If not, digest_obj.digest.length is returned.
221 self.class::DIGEST_LENGTH
225 # digest_obj.length -> integer
226 # digest_obj.size -> integer
228 # Returns digest_obj.digest_length.
229 alias :length :digest_length
230 alias :size :digest_length
233 # digest_obj.block_length -> integer
235 # Returns the block length of the digest.
237 # This method is overridden by each implementation subclass.
239 self.class::BLOCK_LENGTH
246 # Digest::Class.digest(string, #parameters) -> hash_string
248 # Returns the hash value of a given +string+. This is equivalent to
249 # Digest::Class.new(#parameters).digest(string), where extra
250 # +parameters+, if any, are passed through to the constructor and the
251 # +string+ is passed to #digest.
253 raise ArgumentError, "no data given" unless data
254 self.new.digest(data)
257 # Returns the hex-encoded digest value of the given +data+.
258 def hexdigest(data = nil)
259 raise ArgumentError, 'no data given' if data.nil?