summaryrefslogtreecommitdiff
path: root/ext/digest/lib/digest/hmac.rb
blob: 0fbcfe1ae2ef2711518c69705761e2d8b16a8b4f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# == License
#
# Copyright (c) 2006 Akinori MUSHA <knu@iDaemons.org>
#
# Documentation by Akinori MUSHA
#
# All rights reserved.  You can redistribute and/or modify it under
# the same terms as Ruby.
#
#   $Id$
#

warn "use of the experimetal library 'digest/hmac' is discouraged; require 'openssl' and use OpenSSL::HMAC instead." if $VERBOSE

require 'digest'

module Digest
  # = digest/hmac.rb
  #
  # An experimental implementation of HMAC keyed-hashing algorithm
  #
  # == Overview
  #
  # CAUTION: Use of this library is discouraged, because this
  # implementation was meant to be experimental but somehow got into the
  # 1.9 series without being noticed.  Please use OpenSSL::HMAC in the
  # "openssl" library instead.
  #
  # == Examples
  #
  #   require 'digest/hmac'
  #
  #   # one-liner example
  #   puts Digest::HMAC.hexdigest("data", "hash key", Digest::SHA1)
  #
  #   # rather longer one
  #   hmac = Digest::HMAC.new("foo", Digest::RMD160)
  #
  #   buf = ""
  #   while stream.read(16384, buf)
  #     hmac.update(buf)
  #   end
  #
  #   puts hmac.hexdigest
  #
  class HMAC < Digest::Class

    # Creates a Digest::HMAC instance.

    def initialize(key, digester)
      @md = digester.new

      block_len = @md.block_length

      if key.bytesize > block_len
        key = @md.digest(key)
      end

      ipad = Array.new(block_len, 0x36)
      opad = Array.new(block_len, 0x5c)

      key.bytes.each_with_index { |c, i|
        ipad[i] ^= c
        opad[i] ^= c
      }

      @key = key.freeze
      @ipad = ipad.pack('C*').freeze
      @opad = opad.pack('C*').freeze
      @md.update(@ipad)
    end

    def initialize_copy(other) # :nodoc:
      @md = other.instance_eval { @md.clone }
    end

    # call-seq:
    #   hmac.update(string) -> hmac
    #   hmac << string -> hmac
    #
    # Updates the hmac using a given +string+ and returns self.
    def update(text)
      @md.update(text)
      self
    end
    alias << update

    # call-seq:
    #   hmac.reset -> hmac
    #
    # Resets the hmac to the initial state and returns self.
    def reset
      @md.reset
      @md.update(@ipad)
      self
    end

    def finish # :nodoc:
      d = @md.digest!
      @md.update(@opad)
      @md.update(d)
      @md.digest!
    end
    private :finish

    # call-seq:
    #   hmac.digest_length -> Integer
    #
    # Returns the length in bytes of the hash value of the digest.
    def digest_length
      @md.digest_length
    end

    # call-seq:
    #   hmac.block_length -> Integer
    #
    # Returns the block length in bytes of the hmac.
    def block_length
      @md.block_length
    end

    # call-seq:
    #   hmac.inspect -> string
    #
    # Creates a printable version of the hmac object.
    def inspect
      sprintf('#<%s: key=%s, digest=%s>', self.class.name, @key.inspect, @md.inspect.sub(/^\#<(.*)>$/) { $1 });
    end
  end
end