1# == License 2# 3# Copyright (c) 2006 Akinori MUSHA <knu@iDaemons.org> 4# 5# Documentation by Akinori MUSHA 6# 7# All rights reserved. You can redistribute and/or modify it under 8# the same terms as Ruby. 9# 10# $Id: hmac.rb 31594 2011-05-16 20:52:55Z drbrain $ 11# 12 13warn "use of the experimetal library 'digest/hmac' is discouraged; require 'openssl' and use OpenSSL::HMAC instead." if $VERBOSE 14 15require 'digest' 16 17module Digest 18 # = digest/hmac.rb 19 # 20 # An experimental implementation of HMAC keyed-hashing algorithm 21 # 22 # == Overview 23 # 24 # CAUTION: Use of this library is discouraged, because this 25 # implementation was meant to be experimental but somehow got into the 26 # 1.9 series without being noticed. Please use OpenSSL::HMAC in the 27 # "openssl" library instead. 28 # 29 # == Examples 30 # 31 # require 'digest/hmac' 32 # 33 # # one-liner example 34 # puts Digest::HMAC.hexdigest("data", "hash key", Digest::SHA1) 35 # 36 # # rather longer one 37 # hmac = Digest::HMAC.new("foo", Digest::RMD160) 38 # 39 # buf = "" 40 # while stream.read(16384, buf) 41 # hmac.update(buf) 42 # end 43 # 44 # puts hmac.bubblebabble 45 # 46 class HMAC < Digest::Class 47 48 # Creates a Digest::HMAC instance. 49 50 def initialize(key, digester) 51 @md = digester.new 52 53 block_len = @md.block_length 54 55 if key.bytesize > block_len 56 key = @md.digest(key) 57 end 58 59 ipad = Array.new(block_len, 0x36) 60 opad = Array.new(block_len, 0x5c) 61 62 key.bytes.each_with_index { |c, i| 63 ipad[i] ^= c 64 opad[i] ^= c 65 } 66 67 @key = key.freeze 68 @ipad = ipad.pack('C*').freeze 69 @opad = opad.pack('C*').freeze 70 @md.update(@ipad) 71 end 72 73 def initialize_copy(other) # :nodoc: 74 @md = other.instance_eval { @md.clone } 75 end 76 77 # call-seq: 78 # hmac.update(string) -> hmac 79 # hmac << string -> hmac 80 # 81 # Updates the hmac using a given +string+ and returns self. 82 def update(text) 83 @md.update(text) 84 self 85 end 86 alias << update 87 88 # call-seq: 89 # hmac.reset -> hmac 90 # 91 # Resets the hmac to the initial state and returns self. 92 def reset 93 @md.reset 94 @md.update(@ipad) 95 self 96 end 97 98 def finish # :nodoc: 99 d = @md.digest! 100 @md.update(@opad) 101 @md.update(d) 102 @md.digest! 103 end 104 private :finish 105 106 # call-seq: 107 # hmac.digest_length -> Integer 108 # 109 # Returns the length in bytes of the hash value of the digest. 110 def digest_length 111 @md.digest_length 112 end 113 114 # call-seq: 115 # hmac.block_length -> Integer 116 # 117 # Returns the block length in bytes of the hmac. 118 def block_length 119 @md.block_length 120 end 121 122 # call-seq: 123 # hmac.inspect -> string 124 # 125 # Creates a printable version of the hmac object. 126 def inspect 127 sprintf('#<%s: key=%s, digest=%s>', self.class.name, @key.inspect, @md.inspect.sub(/^\#<(.*)>$/) { $1 }); 128 end 129 end 130end 131 132if $0 == __FILE__ 133 eval DATA.gets(nil), nil, $0, DATA.lineno 134end 135 136__END__ 137 138require 'test/unit' 139 140module TM_HMAC 141 def test_s_hexdigest 142 cases.each { |h| 143 digesters.each { |d| 144 assert_equal(h[:hexdigest], Digest::HMAC.hexdigest(h[:data], h[:key], d)) 145 } 146 } 147 end 148 149 def test_hexdigest 150 cases.each { |h| 151 digesters.each { |d| 152 hmac = Digest::HMAC.new(h[:key], d) 153 154 hmac.update(h[:data]) 155 156 assert_equal(h[:hexdigest], hmac.hexdigest) 157 } 158 } 159 end 160 161 def test_reset 162 cases.each { |h| 163 digesters.each { |d| 164 hmac = Digest::HMAC.new(h[:key], d) 165 hmac.update("test") 166 hmac.reset 167 hmac.update(h[:data]) 168 169 assert_equal(h[:hexdigest], hmac.hexdigest) 170 } 171 } 172 end 173end 174 175class TC_HMAC_MD5 < Test::Unit::TestCase 176 include TM_HMAC 177 178 def digesters 179 [Digest::MD5, Digest::MD5.new] 180 end 181 182 # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 183 def cases 184 [ 185 { 186 :key => "\x0b" * 16, 187 :data => "Hi There", 188 :hexdigest => "9294727a3638bb1c13f48ef8158bfc9d", 189 }, { 190 :key => "Jefe", 191 :data => "what do ya want for nothing?", 192 :hexdigest => "750c783e6ab0b503eaa86e310a5db738", 193 }, { 194 :key => "\xaa" * 16, 195 :data => "\xdd" * 50, 196 :hexdigest => "56be34521d144c88dbb8c733f0e8b3f6", 197 }, { 198 :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", 199 :data => "\xcd" * 50, 200 :hexdigest => "697eaf0aca3a3aea3a75164746ffaa79", 201 }, { 202 :key => "\x0c" * 16, 203 :data => "Test With Truncation", 204 :hexdigest => "56461ef2342edc00f9bab995690efd4c", 205 }, { 206 :key => "\xaa" * 80, 207 :data => "Test Using Larger Than Block-Size Key - Hash Key First", 208 :hexdigest => "6b1ab7fe4bd7bf8f0b62e6ce61b9d0cd", 209 }, { 210 :key => "\xaa" * 80, 211 :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 212 :hexdigest => "6f630fad67cda0ee1fb1f562db3aa53e", 213 } 214 ] 215 end 216end 217 218class TC_HMAC_SHA1 < Test::Unit::TestCase 219 include TM_HMAC 220 221 def digesters 222 [Digest::SHA1, Digest::SHA1.new] 223 end 224 225 # Taken from RFC 2202: Test Cases for HMAC-MD5 and HMAC-SHA-1 226 def cases 227 [ 228 { 229 :key => "\x0b" * 20, 230 :data => "Hi There", 231 :hexdigest => "b617318655057264e28bc0b6fb378c8ef146be00", 232 }, { 233 :key => "Jefe", 234 :data => "what do ya want for nothing?", 235 :hexdigest => "effcdf6ae5eb2fa2d27416d5f184df9c259a7c79", 236 }, { 237 :key => "\xaa" * 20, 238 :data => "\xdd" * 50, 239 :hexdigest => "125d7342b9ac11cd91a39af48aa17b4f63f175d3", 240 }, { 241 :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", 242 :data => "\xcd" * 50, 243 :hexdigest => "4c9007f4026250c6bc8414f9bf50c86c2d7235da", 244 }, { 245 :key => "\x0c" * 20, 246 :data => "Test With Truncation", 247 :hexdigest => "4c1a03424b55e07fe7f27be1d58bb9324a9a5a04", 248 }, { 249 :key => "\xaa" * 80, 250 :data => "Test Using Larger Than Block-Size Key - Hash Key First", 251 :hexdigest => "aa4ae5e15272d00e95705637ce8a3b55ed402112", 252 }, { 253 :key => "\xaa" * 80, 254 :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 255 :hexdigest => "e8e99d0f45237d786d6bbaa7965c7808bbff1a91", 256 } 257 ] 258 end 259end 260 261class TC_HMAC_RMD160 < Test::Unit::TestCase 262 include TM_HMAC 263 264 def digesters 265 [Digest::RMD160, Digest::RMD160.new] 266 end 267 268 # Taken from RFC 2286: Test Cases for HMAC-RIPEMD160 and HMAC-RIPEMD128 269 def cases 270 [ 271 { 272 :key => "\x0b" * 20, 273 :data => "Hi There", 274 :hexdigest => "24cb4bd67d20fc1a5d2ed7732dcc39377f0a5668", 275 }, { 276 :key => "Jefe", 277 :data => "what do ya want for nothing?", 278 :hexdigest => "dda6c0213a485a9e24f4742064a7f033b43c4069", 279 }, { 280 :key => "\xaa" * 20, 281 :data => "\xdd" * 50, 282 :hexdigest => "b0b105360de759960ab4f35298e116e295d8e7c1", 283 }, { 284 :key => "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19", 285 :data => "\xcd" * 50, 286 :hexdigest => "d5ca862f4d21d5e610e18b4cf1beb97a4365ecf4", 287 }, { 288 :key => "\x0c" * 20, 289 :data => "Test With Truncation", 290 :hexdigest => "7619693978f91d90539ae786500ff3d8e0518e39", 291 }, { 292 :key => "\xaa" * 80, 293 :data => "Test Using Larger Than Block-Size Key - Hash Key First", 294 :hexdigest => "6466ca07ac5eac29e1bd523e5ada7605b791fd8b", 295 }, { 296 :key => "\xaa" * 80, 297 :data => "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data", 298 :hexdigest => "69ea60798d71616cce5fd0871e23754cd75d5a0a", 299 } 300 ] 301 end 302end 303