rubyntlm 0.6.6 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/README.md +48 -48
  4. data/lib/net/ntlm/blob.rb +11 -10
  5. data/lib/net/ntlm/channel_binding.rb +13 -9
  6. data/lib/net/ntlm/client/session.rb +34 -141
  7. data/lib/net/ntlm/client/session_crypto.rb +126 -0
  8. data/lib/net/ntlm/client.rb +8 -9
  9. data/lib/net/ntlm/encode_util.rb +39 -38
  10. data/lib/net/ntlm/exceptions.rb +3 -0
  11. data/lib/net/ntlm/field.rb +29 -31
  12. data/lib/net/ntlm/field_set.rb +39 -18
  13. data/lib/net/ntlm/int16_le.rb +8 -8
  14. data/lib/net/ntlm/int32_le.rb +8 -7
  15. data/lib/net/ntlm/int64_le.rb +8 -7
  16. data/lib/net/ntlm/md4.rb +102 -64
  17. data/lib/net/ntlm/message/type0.rb +4 -7
  18. data/lib/net/ntlm/message/type1.rb +8 -9
  19. data/lib/net/ntlm/message/type2.rb +87 -67
  20. data/lib/net/ntlm/message/type3.rb +43 -41
  21. data/lib/net/ntlm/message.rb +116 -111
  22. data/lib/net/ntlm/rc4.rb +51 -44
  23. data/lib/net/ntlm/response.rb +112 -0
  24. data/lib/net/ntlm/security_buffer.rb +13 -15
  25. data/lib/net/ntlm/string.rb +8 -7
  26. data/lib/net/ntlm/target_info.rb +52 -44
  27. data/lib/net/ntlm/version.rb +3 -1
  28. data/lib/net/ntlm.rb +37 -125
  29. data/lib/rubyntlm.rb +3 -1
  30. metadata +5 -44
  31. data/.devcontainer/devcontainer.json +0 -22
  32. data/.github/dependabot.yml +0 -10
  33. data/.github/workflows/build.yml +0 -55
  34. data/.gitignore +0 -11
  35. data/.rspec +0 -3
  36. data/Gemfile +0 -20
  37. data/Rakefile +0 -37
  38. data/examples/http.rb +0 -86
  39. data/examples/imap.rb +0 -73
  40. data/examples/smtp.rb +0 -94
  41. data/rubyntlm.gemspec +0 -28
  42. data/spec/lib/net/ntlm/blob_spec.rb +0 -14
  43. data/spec/lib/net/ntlm/channel_binding_spec.rb +0 -15
  44. data/spec/lib/net/ntlm/client/session_spec.rb +0 -85
  45. data/spec/lib/net/ntlm/client_spec.rb +0 -62
  46. data/spec/lib/net/ntlm/encode_util_spec.rb +0 -14
  47. data/spec/lib/net/ntlm/field_set_spec.rb +0 -31
  48. data/spec/lib/net/ntlm/field_spec.rb +0 -32
  49. data/spec/lib/net/ntlm/int16_le_spec.rb +0 -16
  50. data/spec/lib/net/ntlm/int32_le_spec.rb +0 -17
  51. data/spec/lib/net/ntlm/int64_le_spec.rb +0 -17
  52. data/spec/lib/net/ntlm/md4_spec.rb +0 -16
  53. data/spec/lib/net/ntlm/message/type0_spec.rb +0 -19
  54. data/spec/lib/net/ntlm/message/type1_spec.rb +0 -129
  55. data/spec/lib/net/ntlm/message/type2_spec.rb +0 -131
  56. data/spec/lib/net/ntlm/message/type3_spec.rb +0 -336
  57. data/spec/lib/net/ntlm/message_spec.rb +0 -15
  58. data/spec/lib/net/ntlm/security_buffer_spec.rb +0 -62
  59. data/spec/lib/net/ntlm/string_spec.rb +0 -70
  60. data/spec/lib/net/ntlm/target_info_spec.rb +0 -74
  61. data/spec/lib/net/ntlm/version_spec.rb +0 -26
  62. data/spec/lib/net/ntlm_spec.rb +0 -133
  63. data/spec/spec_helper.rb +0 -51
  64. data/spec/support/certificates/sha_256_hash.pem +0 -19
  65. data/spec/support/shared/examples/net/ntlm/field_shared.rb +0 -25
  66. data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +0 -239
  67. data/spec/support/shared/examples/net/ntlm/int_shared.rb +0 -43
  68. data/spec/support/shared/examples/net/ntlm/message_shared.rb +0 -35
@@ -1,48 +1,49 @@
1
- module Net
2
- module NTLM
1
+ # frozen_string_literal: true
3
2
 
4
- class EncodeUtil
5
- if RUBY_VERSION == "1.8.7"
6
- require "kconv"
3
+ module Net
4
+ module NTLM
5
+ # UTF-16LE string encoding and decoding helpers.
6
+ class EncodeUtil
7
+ if RUBY_VERSION == '1.8.7'
8
+ require 'kconv'
7
9
 
8
- # Decode a UTF16 string to a ASCII string
9
- # @param [String] str The string to convert
10
- def self.decode_utf16le(str)
11
- Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
12
- end
10
+ # Decode a UTF16 string to a ASCII string
11
+ # @param [String] str The string to convert
12
+ def self.decode_utf16le(str)
13
+ Kconv.kconv(swap16(str), Kconv::ASCII, Kconv::UTF16)
14
+ end
13
15
 
14
- # Encodes a ASCII string to a UTF16 string
15
- # @param [String] str The string to convert
16
- def self.encode_utf16le(str)
17
- swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
18
- end
16
+ # Encodes a ASCII string to a UTF16 string
17
+ # @param [String] str The string to convert
18
+ def self.encode_utf16le(str)
19
+ swap16(Kconv.kconv(str, Kconv::UTF16, Kconv::ASCII))
20
+ end
19
21
 
20
- # Taggle the strings endianness between big/little and little/big
21
- # @param [String] str The string to swap the endianness on
22
- def self.swap16(str)
23
- str.unpack("v*").pack("n*")
24
- end
25
- else # Use native 1.9 string encoding functions
22
+ # Taggle the strings endianness between big/little and little/big
23
+ # @param [String] str The string to swap the endianness on
24
+ def self.swap16(str)
25
+ str.unpack('v*').pack('n*')
26
+ end
27
+ else # Use native 1.9 string encoding functions
26
28
 
27
- # Decode a UTF16 string to a ASCII string
28
- # @param [String] str The string to convert
29
- def self.decode_utf16le(str)
30
- str = str.dup.force_encoding(Encoding::UTF_16LE)
31
- str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
32
- end
29
+ # Decode a UTF16 string to a ASCII string
30
+ # @param [String] str The string to convert
31
+ def self.decode_utf16le(str)
32
+ str = str.dup.force_encoding(Encoding::UTF_16LE)
33
+ str.encode(Encoding::UTF_8, Encoding::UTF_16LE).force_encoding('UTF-8')
34
+ end
33
35
 
34
- # Encodes a ASCII string to a UTF16 string
35
- # @param [String] str The string to convert
36
- # @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
37
- # encodings. This library uses string contatination to build the packet bytes. The end result is that
38
- # you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
39
- # the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
40
- # concatination works seamlessly.
41
- def self.encode_utf16le(str)
42
- str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
36
+ # Encodes a ASCII string to a UTF16 string
37
+ # @param [String] str The string to convert
38
+ # @note This implementation may seem stupid but the problem is that UTF16-LE and UTF-8 are incompatiable
39
+ # encodings. This library uses string contatination to build the packet bytes. The end result is that
40
+ # you can either marshal the encodings elsewhere of simply know that each time you call encode_utf16le
41
+ # the function will convert the string bytes to UTF-16LE and note the encoding as UTF-8 so that byte
42
+ # concatination works seamlessly.
43
+ def self.encode_utf16le(str)
44
+ str.dup.force_encoding('UTF-8').encode(Encoding::UTF_16LE, Encoding::UTF_8).force_encoding('ASCII-8BIT')
45
+ end
43
46
  end
44
47
  end
45
48
  end
46
-
47
- end
48
49
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
5
  class NtlmError < StandardError; end
4
6
 
7
+ # Raised when target info AV_PAIR data cannot be parsed.
5
8
  class InvalidTargetDataError < NtlmError
6
9
  attr_reader :data
7
10
 
@@ -1,35 +1,33 @@
1
- module Net
2
- module NTLM
3
-
4
- # base classes for primitives
5
- # @private
6
- class Field
7
- attr_accessor :active, :value
8
-
9
- def initialize(opts)
10
- @value = opts[:value]
11
- @active = opts[:active].nil? ? true : opts[:active]
12
- @size = opts[:size].nil? ? 0 : opts[:size]
13
- end
14
-
15
- def size
16
- @active ? @size : 0
17
- end
1
+ # frozen_string_literal: true
18
2
 
19
- # Serializer function for field data
20
- # Exists in this class to be overridden by child classes
21
- def serialize
22
- raise NotImplementedError
23
- end
24
-
25
- # Parser function for field data
26
- # Exists in this class to be overridden by child classes
27
- def parse(str, offset=0)
28
- raise NotImplementedError
3
+ module Net
4
+ module NTLM
5
+ # base classes for primitives
6
+ # @private
7
+ class Field
8
+ attr_accessor :active, :value
9
+
10
+ def initialize(opts)
11
+ @value = opts[:value]
12
+ @active = opts[:active].nil? || opts[:active]
13
+ @size = opts[:size].nil? ? 0 : opts[:size]
14
+ end
15
+
16
+ def size
17
+ @active ? @size : 0
18
+ end
19
+
20
+ # Serializer function for field data
21
+ # Exists in this class to be overridden by child classes
22
+ def serialize
23
+ raise NotImplementedError
24
+ end
25
+
26
+ # Parser function for field data
27
+ # Exists in this class to be overridden by child classes
28
+ def parse(str, offset = 0)
29
+ raise NotImplementedError
30
+ end
29
31
  end
30
-
31
32
  end
32
-
33
-
34
33
  end
35
- end
@@ -1,10 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
-
4
5
  # base class of data structure
5
6
  class FieldSet
6
7
  class << FieldSet
7
-
8
8
  # @macro string_security_buffer
9
9
  # @method $1
10
10
  # @method $1=
@@ -17,26 +17,41 @@ module Net
17
17
  # @method $1
18
18
  # @method $1=
19
19
  # @return [Int16LE]
20
- def int16LE(name, opts)
20
+ def int16_le(name, opts)
21
21
  add_field(name, Net::NTLM::Int16LE, opts)
22
22
  end
23
23
 
24
+ # Legacy camelCase alias for {.int16_le}; kept for backward compatibility.
25
+ # rubocop:disable Naming/MethodName
26
+ alias int16LE int16_le
27
+ # rubocop:enable Naming/MethodName
28
+
24
29
  # @macro int32le_security_buffer
25
30
  # @method $1
26
31
  # @method $1=
27
32
  # @return [Int32LE]
28
- def int32LE(name, opts)
33
+ def int32_le(name, opts)
29
34
  add_field(name, Net::NTLM::Int32LE, opts)
30
35
  end
31
36
 
37
+ # Legacy camelCase alias for {.int32_le}; kept for backward compatibility.
38
+ # rubocop:disable Naming/MethodName
39
+ alias int32LE int32_le
40
+ # rubocop:enable Naming/MethodName
41
+
32
42
  # @macro int64le_security_buffer
33
43
  # @method $1
34
44
  # @method $1=
35
45
  # @return [Int64]
36
- def int64LE(name, opts)
46
+ def int64_le(name, opts)
37
47
  add_field(name, Net::NTLM::Int64LE, opts)
38
48
  end
39
49
 
50
+ # Legacy camelCase alias for {.int64_le}; kept for backward compatibility.
51
+ # rubocop:disable Naming/MethodName
52
+ alias int64LE int64_le
53
+ # rubocop:enable Naming/MethodName
54
+
40
55
  # @macro security_buffer
41
56
  # @method $1
42
57
  # @method $1=
@@ -51,17 +66,20 @@ module Net
51
66
 
52
67
  def names
53
68
  return [] if @proto.nil?
54
- @proto.map{|n, t, o| n}
69
+
70
+ @proto.map { |n, _t, _o| n }
55
71
  end
56
72
 
57
73
  def types
58
74
  return [] if @proto.nil?
59
- @proto.map{|n, t, o| t}
75
+
76
+ @proto.map { |_n, t, _o| t }
60
77
  end
61
78
 
62
79
  def opts
63
80
  return [] if @proto.nil?
64
- @proto.map{|n, t, o| o}
81
+
82
+ @proto.map { |_n, _t, o| o }
65
83
  end
66
84
 
67
85
  private
@@ -72,7 +90,7 @@ module Net
72
90
  end
73
91
 
74
92
  def define_accessor(name)
75
- module_eval(<<-End, __FILE__, __LINE__ + 1)
93
+ module_eval(<<-RUBY, __FILE__, __LINE__ + 1)
76
94
  def #{name}
77
95
  self['#{name}'].value
78
96
  end
@@ -80,35 +98,37 @@ module Net
80
98
  def #{name}=(val)
81
99
  self['#{name}'].value = val
82
100
  end
83
- End
101
+ RUBY
84
102
  end
85
103
  end
86
104
 
87
105
  def initialize
88
- @alist = self.class.prototypes.map{ |n, t, o| [n, t.new(o)] }
106
+ @alist = self.class.prototypes.map { |n, t, o| [n, t.new(o)] }
89
107
  end
90
108
 
91
- def parse(str, offset=0)
92
- @alist.inject(offset){|cur, a| cur += a[1].parse(str, cur)}
109
+ def parse(str, offset = 0)
110
+ @alist.inject(offset) { |cur, a| cur + a[1].parse(str, cur) }
93
111
  end
94
112
 
95
113
  def serialize
96
- @alist.map{|n, f| f.serialize }.join
114
+ @alist.map { |_n, f| f.serialize }.join
97
115
  end
98
116
 
99
117
  def size
100
- @alist.inject(0){|sum, a| sum += a[1].size}
118
+ @alist.inject(0) { |sum, a| sum + a[1].size }
101
119
  end
102
120
 
103
121
  def [](name)
104
122
  a = @alist.assoc(name.to_s.intern)
105
123
  raise ArgumentError, "no such field: #{name}" unless a
124
+
106
125
  a[1]
107
126
  end
108
127
 
109
128
  def []=(name, val)
110
129
  a = @alist.assoc(name.to_s.intern)
111
130
  raise ArgumentError, "no such field: #{name}" unless a
131
+
112
132
  a[1] = val
113
133
  end
114
134
 
@@ -120,10 +140,11 @@ module Net
120
140
  self[name].active = false
121
141
  end
122
142
 
123
- def has_disabled_fields?
124
- @alist.any? { |name, field| !field.active }
143
+ def disabled_fields?
144
+ @alist.any? { |_name, field| !field.active }
125
145
  end
126
- end
127
146
 
147
+ alias has_disabled_fields? disabled_fields?
148
+ end
128
149
  end
129
150
  end
@@ -1,16 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
-
5
+ # 16-bit little-endian integer field.
4
6
  class Int16LE < Field
5
-
6
7
  def initialize(opt)
7
8
  super(opt)
8
9
  @size = 2
9
10
  end
10
11
 
11
- def parse(str, offset=0)
12
- if @active and str.size >= offset + @size
13
- @value = str[offset, @size].unpack("v")[0]
12
+ def parse(str, offset = 0)
13
+ if @active && str.size >= offset + @size
14
+ @value = str[offset, @size].unpack1('v')
14
15
  @size
15
16
  else
16
17
  0
@@ -18,9 +19,8 @@ module Net
18
19
  end
19
20
 
20
21
  def serialize
21
- [@value].pack("v")
22
+ [@value].pack('v')
22
23
  end
23
24
  end
24
-
25
25
  end
26
- end
26
+ end
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
-
5
+ # 32-bit little-endian integer field.
4
6
  class Int32LE < Field
5
7
  def initialize(opt)
6
8
  super(opt)
7
9
  @size = 4
8
10
  end
9
11
 
10
- def parse(str, offset=0)
11
- if @active and str.size >= offset + @size
12
- @value = str.slice(offset, @size).unpack("V")[0]
12
+ def parse(str, offset = 0)
13
+ if @active && str.size >= offset + @size
14
+ @value = str.slice(offset, @size).unpack1('V')
13
15
  @size
14
16
  else
15
17
  0
@@ -17,9 +19,8 @@ module Net
17
19
  end
18
20
 
19
21
  def serialize
20
- [@value].pack("V") if @active
22
+ [@value].pack('V') if @active
21
23
  end
22
24
  end
23
-
24
25
  end
25
- end
26
+ end
@@ -1,15 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
-
5
+ # 64-bit little-endian integer field.
4
6
  class Int64LE < Field
5
7
  def initialize(opt)
6
8
  super(opt)
7
9
  @size = 8
8
10
  end
9
11
 
10
- def parse(str, offset=0)
11
- if @active and str.size >= offset + @size
12
- d, u = str.slice(offset, @size).unpack("V2")
12
+ def parse(str, offset = 0)
13
+ if @active && str.size >= offset + @size
14
+ d, u = str.slice(offset, @size).unpack('V2')
13
15
  @value = (u * 0x100000000 + d)
14
16
  @size
15
17
  else
@@ -18,9 +20,8 @@ module Net
18
20
  end
19
21
 
20
22
  def serialize
21
- [@value & 0x00000000ffffffff, @value >> 32].pack("V2") if @active
23
+ [@value & 0x00000000ffffffff, @value >> 32].pack('V2') if @active
22
24
  end
23
25
  end
24
-
25
26
  end
26
- end
27
+ end
data/lib/net/ntlm/md4.rb CHANGED
@@ -1,81 +1,119 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'openssl'
2
4
 
3
5
  module Net
4
- module NTLM
5
-
6
- class Md4
6
+ # Pure-Ruby MD4 digest, used to compute the NTLM hash.
7
+ module NTLM
8
+ # MD4 message digest.
9
+ class Md4
10
+ begin
11
+ OpenSSL::Digest.digest('MD4', '')
12
+ rescue StandardError
13
+ # libssl-3.0+ doesn't support legacy MD4 -> use our own implementation
7
14
 
8
- begin
9
- OpenSSL::Digest::MD4.digest("")
10
- rescue
11
- # libssl-3.0+ doesn't support legacy MD4 -> use our own implementation
15
+ require 'stringio'
12
16
 
13
- require 'stringio'
17
+ MD4_MASK = (1 << 32) - 1
18
+ MD4_INITIAL_STATE = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476].freeze
14
19
 
15
- def self.digest(string)
16
- # functions
17
- mask = (1 << 32) - 1
18
- f = proc {|x, y, z| x & y | x.^(mask) & z}
19
- g = proc {|x, y, z| x & y | x & z | y & z}
20
- h = proc {|x, y, z| x ^ y ^ z}
21
- r = proc {|v, s| (v << s).&(mask) | (v.&(mask) >> (32 - s))}
20
+ def self.digest(string)
21
+ state = MD4_INITIAL_STATE.dup
22
+ io = StringIO.new(md4_pad(string))
23
+ block = +''
22
24
 
23
- # initial hash
24
- a, b, c, d = 0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476
25
+ while io.read(64, block)
26
+ words = block.unpack('V16')
27
+ md4_process_block(state, words)
28
+ end
25
29
 
26
- string = string.b
27
- bit_len = string.bytesize << 3
28
- string += "\x80".b
29
- while (string.size % 64) != 56
30
- string += "\0"
30
+ state.pack('V4')
31
31
  end
32
- string = string.force_encoding('ascii-8bit') + [bit_len & mask, bit_len >> 32].pack("V2")
33
32
 
34
- if string.size % 64 != 0
35
- fail "failed to pad to correct length"
36
- end
33
+ class << self
34
+ private
37
35
 
38
- io = StringIO.new(string)
39
- block = ""
40
-
41
- while io.read(64, block)
42
- x = block.unpack("V16")
43
-
44
- # Process this block.
45
- aa, bb, cc, dd = a, b, c, d
46
- [0, 4, 8, 12].each {|i|
47
- a = r[a + f[b, c, d] + x[i], 3]; i += 1
48
- d = r[d + f[a, b, c] + x[i], 7]; i += 1
49
- c = r[c + f[d, a, b] + x[i], 11]; i += 1
50
- b = r[b + f[c, d, a] + x[i], 19]
51
- }
52
- [0, 1, 2, 3].each {|i|
53
- a = r[a + g[b, c, d] + x[i] + 0x5a827999, 3]; i += 4
54
- d = r[d + g[a, b, c] + x[i] + 0x5a827999, 5]; i += 4
55
- c = r[c + g[d, a, b] + x[i] + 0x5a827999, 9]; i += 4
56
- b = r[b + g[c, d, a] + x[i] + 0x5a827999, 13]
57
- }
58
- [0, 2, 1, 3].each {|i|
59
- a = r[a + h[b, c, d] + x[i] + 0x6ed9eba1, 3]; i += 8
60
- d = r[d + h[a, b, c] + x[i] + 0x6ed9eba1, 9]; i -= 4
61
- c = r[c + h[d, a, b] + x[i] + 0x6ed9eba1, 11]; i += 8
62
- b = r[b + h[c, d, a] + x[i] + 0x6ed9eba1, 15]
63
- }
64
- a = (a + aa) & mask
65
- b = (b + bb) & mask
66
- c = (c + cc) & mask
67
- d = (d + dd) & mask
68
- end
36
+ def md4_pad(string)
37
+ string = string.b
38
+ bit_len = string.bytesize << 3
39
+ string += "\x80".b
40
+ string += "\0" while (string.size % 64) != 56
41
+ string = string.force_encoding('ascii-8bit') + [bit_len & MD4_MASK, bit_len >> 32].pack('V2')
69
42
 
70
- [a, b, c, d].pack("V4")
71
- end
43
+ raise 'failed to pad to correct length' if string.size % 64 != 0
44
+
45
+ string
46
+ end
47
+
48
+ def md4_process_block(state, words)
49
+ saved = state.dup
50
+ md4_round1(state, words)
51
+ md4_round2(state, words)
52
+ md4_round3(state, words)
53
+ state.map!.with_index { |word, index| (word + saved[index]) & MD4_MASK }
54
+ end
55
+
56
+ def md4_round1(state, words)
57
+ mix = method(:md4_f)
58
+ 4.times do |round|
59
+ base = round * 4
60
+ md4_step(state, 0, words[base], 3, mix)
61
+ md4_step(state, 3, words[base + 1], 7, mix)
62
+ md4_step(state, 2, words[base + 2], 11, mix)
63
+ md4_step(state, 1, words[base + 3], 19, mix)
64
+ end
65
+ state
66
+ end
67
+
68
+ def md4_round2(state, words)
69
+ mix = ->(first, second, third) { md4_g(first, second, third) + 0x5a827999 }
70
+ 4.times do |round|
71
+ md4_step(state, 0, words[round], 3, mix)
72
+ md4_step(state, 3, words[round + 4], 5, mix)
73
+ md4_step(state, 2, words[round + 8], 9, mix)
74
+ md4_step(state, 1, words[round + 12], 13, mix)
75
+ end
76
+ state
77
+ end
72
78
 
73
- else
74
- # Openssl/libssl provides MD4, so we can use it.
75
- def self.digest(string)
76
- OpenSSL::Digest::MD4.digest(string)
79
+ def md4_round3(state, words)
80
+ mix = ->(first, second, third) { md4_h(first, second, third) + 0x6ed9eba1 }
81
+ [0, 2, 1, 3].each do |round|
82
+ md4_step(state, 0, words[round], 3, mix)
83
+ md4_step(state, 3, words[round + 8], 9, mix)
84
+ md4_step(state, 2, words[round + 4], 11, mix)
85
+ md4_step(state, 1, words[round + 12], 15, mix)
86
+ end
87
+ state
88
+ end
89
+
90
+ def md4_step(state, target, word, shift, mix)
91
+ others = [state[(target + 1) % 4], state[(target + 2) % 4], state[(target + 3) % 4]]
92
+ state[target] = md4_rotate(state[target] + mix.call(*others) + word, shift)
93
+ end
94
+
95
+ def md4_f(first, second, third)
96
+ (first & second) | ((first ^ MD4_MASK) & third)
97
+ end
98
+
99
+ def md4_g(first, second, third)
100
+ (first & second) | (first & third) | (second & third)
101
+ end
102
+
103
+ def md4_h(first, second, third)
104
+ first ^ second ^ third
105
+ end
106
+
107
+ def md4_rotate(value, shift)
108
+ ((value << shift) & MD4_MASK) | ((value & MD4_MASK) >> (32 - shift))
109
+ end
110
+ end
111
+ else
112
+ # Openssl/libssl provides MD4, so we can use it.
113
+ def self.digest(string)
114
+ OpenSSL::Digest::MD4.digest(string)
115
+ end
77
116
  end
78
117
  end
79
118
  end
80
119
  end
81
- end
@@ -1,16 +1,13 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
5
  class Message
4
-
5
6
  # sub class definitions
6
7
  class Type0 < Message
7
- string :sign, {:size => 8, :value => SSP_SIGN}
8
- int32LE :type, {:value => 0}
8
+ string :sign, { size: 8, value: SSP_SIGN }
9
+ int32_le :type, { value: 0 }
9
10
  end
10
-
11
-
12
11
  end
13
12
  end
14
13
  end
15
-
16
-
@@ -1,17 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
4
  module NTLM
3
5
  class Message
4
-
5
6
  # @private false
6
7
  class Type1 < Message
7
-
8
- string :sign, {:size => 8, :value => SSP_SIGN}
9
- int32LE :type, {:value => 1}
10
- int32LE :flag, {:value => DEFAULT_FLAGS[:TYPE1] }
11
- security_buffer :domain, {:value => ""}
12
- security_buffer :workstation, {:value => Socket.gethostname }
13
- string :os_version, {:size => 8, :value => "", :active => false }
14
-
8
+ string :sign, { size: 8, value: SSP_SIGN }
9
+ int32_le :type, { value: 1 }
10
+ int32_le :flag, { value: DEFAULT_FLAGS[:TYPE1] }
11
+ security_buffer :domain, { value: '' }
12
+ security_buffer :workstation, { value: Socket.gethostname }
13
+ string :os_version, { size: 8, value: '', active: false }
15
14
  end
16
15
  end
17
16
  end