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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/README.md +48 -48
- data/lib/net/ntlm/blob.rb +11 -10
- data/lib/net/ntlm/channel_binding.rb +13 -9
- data/lib/net/ntlm/client/session.rb +34 -141
- data/lib/net/ntlm/client/session_crypto.rb +126 -0
- data/lib/net/ntlm/client.rb +8 -9
- data/lib/net/ntlm/encode_util.rb +39 -38
- data/lib/net/ntlm/exceptions.rb +3 -0
- data/lib/net/ntlm/field.rb +29 -31
- data/lib/net/ntlm/field_set.rb +39 -18
- data/lib/net/ntlm/int16_le.rb +8 -8
- data/lib/net/ntlm/int32_le.rb +8 -7
- data/lib/net/ntlm/int64_le.rb +8 -7
- data/lib/net/ntlm/md4.rb +102 -64
- data/lib/net/ntlm/message/type0.rb +4 -7
- data/lib/net/ntlm/message/type1.rb +8 -9
- data/lib/net/ntlm/message/type2.rb +87 -67
- data/lib/net/ntlm/message/type3.rb +43 -41
- data/lib/net/ntlm/message.rb +116 -111
- data/lib/net/ntlm/rc4.rb +51 -44
- data/lib/net/ntlm/response.rb +112 -0
- data/lib/net/ntlm/security_buffer.rb +13 -15
- data/lib/net/ntlm/string.rb +8 -7
- data/lib/net/ntlm/target_info.rb +52 -44
- data/lib/net/ntlm/version.rb +3 -1
- data/lib/net/ntlm.rb +37 -125
- data/lib/rubyntlm.rb +3 -1
- metadata +5 -44
- data/.devcontainer/devcontainer.json +0 -22
- data/.github/dependabot.yml +0 -10
- data/.github/workflows/build.yml +0 -55
- data/.gitignore +0 -11
- data/.rspec +0 -3
- data/Gemfile +0 -20
- data/Rakefile +0 -37
- data/examples/http.rb +0 -86
- data/examples/imap.rb +0 -73
- data/examples/smtp.rb +0 -94
- data/rubyntlm.gemspec +0 -28
- data/spec/lib/net/ntlm/blob_spec.rb +0 -14
- data/spec/lib/net/ntlm/channel_binding_spec.rb +0 -15
- data/spec/lib/net/ntlm/client/session_spec.rb +0 -85
- data/spec/lib/net/ntlm/client_spec.rb +0 -62
- data/spec/lib/net/ntlm/encode_util_spec.rb +0 -14
- data/spec/lib/net/ntlm/field_set_spec.rb +0 -31
- data/spec/lib/net/ntlm/field_spec.rb +0 -32
- data/spec/lib/net/ntlm/int16_le_spec.rb +0 -16
- data/spec/lib/net/ntlm/int32_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/int64_le_spec.rb +0 -17
- data/spec/lib/net/ntlm/md4_spec.rb +0 -16
- data/spec/lib/net/ntlm/message/type0_spec.rb +0 -19
- data/spec/lib/net/ntlm/message/type1_spec.rb +0 -129
- data/spec/lib/net/ntlm/message/type2_spec.rb +0 -131
- data/spec/lib/net/ntlm/message/type3_spec.rb +0 -336
- data/spec/lib/net/ntlm/message_spec.rb +0 -15
- data/spec/lib/net/ntlm/security_buffer_spec.rb +0 -62
- data/spec/lib/net/ntlm/string_spec.rb +0 -70
- data/spec/lib/net/ntlm/target_info_spec.rb +0 -74
- data/spec/lib/net/ntlm/version_spec.rb +0 -26
- data/spec/lib/net/ntlm_spec.rb +0 -133
- data/spec/spec_helper.rb +0 -51
- data/spec/support/certificates/sha_256_hash.pem +0 -19
- data/spec/support/shared/examples/net/ntlm/field_shared.rb +0 -25
- data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +0 -239
- data/spec/support/shared/examples/net/ntlm/int_shared.rb +0 -43
- data/spec/support/shared/examples/net/ntlm/message_shared.rb +0 -35
data/lib/net/ntlm/encode_util.rb
CHANGED
|
@@ -1,48 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
module NTLM
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
data/lib/net/ntlm/exceptions.rb
CHANGED
data/lib/net/ntlm/field.rb
CHANGED
|
@@ -1,35 +1,33 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
data/lib/net/ntlm/field_set.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
-
|
|
69
|
+
|
|
70
|
+
@proto.map { |n, _t, _o| n }
|
|
55
71
|
end
|
|
56
72
|
|
|
57
73
|
def types
|
|
58
74
|
return [] if @proto.nil?
|
|
59
|
-
|
|
75
|
+
|
|
76
|
+
@proto.map { |_n, t, _o| t }
|
|
60
77
|
end
|
|
61
78
|
|
|
62
79
|
def opts
|
|
63
80
|
return [] if @proto.nil?
|
|
64
|
-
|
|
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(<<-
|
|
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
|
-
|
|
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
|
|
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{|
|
|
114
|
+
@alist.map { |_n, f| f.serialize }.join
|
|
97
115
|
end
|
|
98
116
|
|
|
99
117
|
def size
|
|
100
|
-
@alist.inject(0){|sum, a| sum
|
|
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
|
|
124
|
-
@alist.any? { |
|
|
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
|
data/lib/net/ntlm/int16_le.rb
CHANGED
|
@@ -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
|
|
13
|
-
@value = str[offset, @size].
|
|
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(
|
|
22
|
+
[@value].pack('v')
|
|
22
23
|
end
|
|
23
24
|
end
|
|
24
|
-
|
|
25
25
|
end
|
|
26
|
-
end
|
|
26
|
+
end
|
data/lib/net/ntlm/int32_le.rb
CHANGED
|
@@ -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
|
|
12
|
-
@value = str.slice(offset, @size).
|
|
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(
|
|
22
|
+
[@value].pack('V') if @active
|
|
21
23
|
end
|
|
22
24
|
end
|
|
23
|
-
|
|
24
25
|
end
|
|
25
|
-
end
|
|
26
|
+
end
|
data/lib/net/ntlm/int64_le.rb
CHANGED
|
@@ -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
|
|
12
|
-
d, u = str.slice(offset, @size).unpack(
|
|
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(
|
|
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
|
-
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
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
|
-
|
|
17
|
+
MD4_MASK = (1 << 32) - 1
|
|
18
|
+
MD4_INITIAL_STATE = [0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476].freeze
|
|
14
19
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
|
|
24
|
-
|
|
25
|
+
while io.read(64, block)
|
|
26
|
+
words = block.unpack('V16')
|
|
27
|
+
md4_process_block(state, words)
|
|
28
|
+
end
|
|
25
29
|
|
|
26
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
end
|
|
33
|
+
class << self
|
|
34
|
+
private
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
71
|
-
|
|
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
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
|
8
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
security_buffer :
|
|
12
|
-
|
|
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
|