rubyntlm 0.5.3 → 0.6.0

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 (57) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -3
  3. data/.rspec +2 -2
  4. data/.travis.yml +10 -11
  5. data/CHANGELOG.md +5 -5
  6. data/Gemfile +3 -3
  7. data/LICENSE +19 -19
  8. data/Rakefile +22 -22
  9. data/lib/net/ntlm.rb +266 -263
  10. data/lib/net/ntlm/blob.rb +28 -28
  11. data/lib/net/ntlm/channel_binding.rb +65 -0
  12. data/lib/net/ntlm/client.rb +65 -65
  13. data/lib/net/ntlm/client/session.rb +237 -223
  14. data/lib/net/ntlm/encode_util.rb +49 -49
  15. data/lib/net/ntlm/exceptions.rb +14 -0
  16. data/lib/net/ntlm/field.rb +34 -34
  17. data/lib/net/ntlm/field_set.rb +129 -129
  18. data/lib/net/ntlm/int16_le.rb +25 -25
  19. data/lib/net/ntlm/int32_le.rb +24 -24
  20. data/lib/net/ntlm/int64_le.rb +25 -25
  21. data/lib/net/ntlm/message.rb +129 -129
  22. data/lib/net/ntlm/message/type0.rb +16 -16
  23. data/lib/net/ntlm/message/type1.rb +18 -18
  24. data/lib/net/ntlm/message/type2.rb +102 -102
  25. data/lib/net/ntlm/message/type3.rb +131 -131
  26. data/lib/net/ntlm/security_buffer.rb +47 -47
  27. data/lib/net/ntlm/string.rb +34 -34
  28. data/lib/net/ntlm/target_info.rb +89 -0
  29. data/lib/net/ntlm/version.rb +11 -11
  30. data/rubyntlm.gemspec +28 -28
  31. data/spec/lib/net/ntlm/blob_spec.rb +16 -16
  32. data/spec/lib/net/ntlm/channel_binding_spec.rb +17 -0
  33. data/spec/lib/net/ntlm/client/session_spec.rb +68 -68
  34. data/spec/lib/net/ntlm/client_spec.rb +64 -64
  35. data/spec/lib/net/ntlm/encode_util_spec.rb +16 -16
  36. data/spec/lib/net/ntlm/field_set_spec.rb +33 -33
  37. data/spec/lib/net/ntlm/field_spec.rb +34 -34
  38. data/spec/lib/net/ntlm/int16_le_spec.rb +17 -17
  39. data/spec/lib/net/ntlm/int32_le_spec.rb +18 -18
  40. data/spec/lib/net/ntlm/int64_le_spec.rb +18 -18
  41. data/spec/lib/net/ntlm/message/type0_spec.rb +20 -20
  42. data/spec/lib/net/ntlm/message/type1_spec.rb +131 -131
  43. data/spec/lib/net/ntlm/message/type2_spec.rb +132 -132
  44. data/spec/lib/net/ntlm/message/type3_spec.rb +225 -225
  45. data/spec/lib/net/ntlm/message_spec.rb +16 -16
  46. data/spec/lib/net/ntlm/security_buffer_spec.rb +64 -64
  47. data/spec/lib/net/ntlm/string_spec.rb +72 -72
  48. data/spec/lib/net/ntlm/target_info_spec.rb +76 -0
  49. data/spec/lib/net/ntlm/version_spec.rb +27 -27
  50. data/spec/lib/net/ntlm_spec.rb +127 -127
  51. data/spec/spec_helper.rb +22 -22
  52. data/spec/support/certificates/sha_256_hash.pem +19 -0
  53. data/spec/support/shared/examples/net/ntlm/field_shared.rb +25 -25
  54. data/spec/support/shared/examples/net/ntlm/fieldset_shared.rb +239 -239
  55. data/spec/support/shared/examples/net/ntlm/int_shared.rb +43 -43
  56. data/spec/support/shared/examples/net/ntlm/message_shared.rb +35 -35
  57. metadata +12 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 044c8c3cccfa69d9397e63b89263b2ae5abef45d
4
- data.tar.gz: d9b2964d47468ea490d127ba88dd2579a817cb5b
3
+ metadata.gz: 96ed497ebb6619b61911e39777bf3d193909bc45
4
+ data.tar.gz: 4396365cd33577c1b52db0ae516d8c1353293877
5
5
  SHA512:
6
- metadata.gz: 6ffeaeb30b32bddab3468fe985657bf508a04e27f4327c26767a58d170bb8293c683e4d55cfdeeb78609deb26e7b3392c46312298adbe5867bffe5b50d86b3b5
7
- data.tar.gz: 599b495aa2de031410ec8f630809e1017b2c79f912a5178aa53ee30d0a65f98ac1b965b108dd6a2068009f6b0cf10774cd345352313c97696bb2089352a96b60
6
+ metadata.gz: 5959b911914ac4b71dc44296ee98345f874db4786da7630db08d016856bfa5555103e94310dd1bb0252211646c45ff87a3f94a9399be939e35ab0c8d32e067be
7
+ data.tar.gz: 1ab87702641e2e9946527ee504020fad142445ce6ac78206102b3fa378550a9c44ebb51795089f081e72b56329234dd772c8da7281f44badfe976d28dbc47c73
data/.gitignore CHANGED
@@ -1,3 +1,3 @@
1
- .yardoc
2
- /doc
3
- Gemfile.lock
1
+ .yardoc
2
+ /doc
3
+ Gemfile.lock
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
- --format documentation
2
- --color
1
+ --format documentation
2
+ --color
@@ -1,11 +1,10 @@
1
- language: ruby
2
- rvm:
3
- - 1.9.3
4
- - 1.9.2
5
- - 1.8.7
6
- - 2.0.0
7
- - rbx-19mode
8
- - rbx-18mode
9
- - jruby-19mode
10
- before_install:
11
- - gem update bundler
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 1.9.2
5
+ - 2.0.0
6
+ - rbx-19mode
7
+ - rbx-18mode
8
+ - jruby-19mode
9
+ before_install:
10
+ - gem update bundler
@@ -1,6 +1,6 @@
1
- 0.2.0 - Major changes to behavior!!!!
2
- - Bug - Type 1 packets do not include a domain and workstation by defauly. Packet capture software will see this type of packet as malformed. All packets now include this information
3
- - Bug - Type 3 packets do not include the calling workstation. This should be setup by default.
4
-
5
- 0.1.2
1
+ 0.2.0 - Major changes to behavior!!!!
2
+ - Bug - Type 1 packets do not include a domain and workstation by defauly. Packet capture software will see this type of packet as malformed. All packets now include this information
3
+ - Bug - Type 3 packets do not include the calling workstation. This should be setup by default.
4
+
5
+ 0.1.2
6
6
  - Feature user can specify the target domain
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source 'https://rubygems.org'
2
-
3
- gemspec
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE CHANGED
@@ -1,20 +1,20 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2013 Paul Morton, Matt Zukowski, Kohei Kajimoto
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy of
6
- this software and associated documentation files (the "Software"), to deal in
7
- the Software without restriction, including without limitation the rights to
8
- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
- the Software, and to permit persons to whom the Software is furnished to do so,
10
- subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
- FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
- COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 Paul Morton, Matt Zukowski, Kohei Kajimoto
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
20
  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile CHANGED
@@ -1,22 +1,22 @@
1
- require "bundler/gem_tasks"
2
-
3
-
4
- require 'rspec/core/rake_task'
5
- RSpec::Core::RakeTask.new(:spec)
6
-
7
- task :default => :spec
8
-
9
- desc "Generate code coverage"
10
- task :coverage do
11
- ENV['COVERAGE'] = 'true'
12
- Rake::Task["spec"].execute
13
- end
14
-
15
- desc "Open a Pry console for this library"
16
- task :console do
17
- require 'pry'
18
- require 'net/ntlm'
19
- ARGV.clear
20
- Pry.start
21
- end
22
-
1
+ require "bundler/gem_tasks"
2
+
3
+
4
+ require 'rspec/core/rake_task'
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => :spec
8
+
9
+ desc "Generate code coverage"
10
+ task :coverage do
11
+ ENV['COVERAGE'] = 'true'
12
+ Rake::Task["spec"].execute
13
+ end
14
+
15
+ desc "Open a Pry console for this library"
16
+ task :console do
17
+ require 'pry'
18
+ require 'net/ntlm'
19
+ ARGV.clear
20
+ Pry.start
21
+ end
22
+
@@ -1,263 +1,266 @@
1
- # encoding: UTF-8
2
- #
3
- # = net/ntlm.rb
4
- #
5
- # An NTLM Authentication Library for Ruby
6
- #
7
- # This code is a derivative of "dbf2.rb" written by yrock
8
- # and Minero Aoki. You can find original code here:
9
- # http://jp.rubyist.net/magazine/?0013-CodeReview
10
- # -------------------------------------------------------------
11
- # Copyright (c) 2005,2006 yrock
12
- #
13
- #
14
- # 2006-02-11 refactored by Minero Aoki
15
- # -------------------------------------------------------------
16
- #
17
- # All protocol information used to write this code stems from
18
- # "The NTLM Authentication Protocol" by Eric Glass. The author
19
- # would thank to him for this tremendous work and making it
20
- # available on the net.
21
- # http://davenport.sourceforge.net/ntlm.html
22
- # -------------------------------------------------------------
23
- # Copyright (c) 2003 Eric Glass
24
- #
25
- # -------------------------------------------------------------
26
- #
27
- # The author also looked Mozilla-Firefox-1.0.7 source code,
28
- # namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
29
- # Jonathan Bastien-Filiatrault's libntlm-ruby.
30
- # "http://x2a.org/websvn/filedetails.php?
31
- # repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
32
- # The latter has a minor bug in its separate_keys function.
33
- # The third key has to begin from the 14th character of the
34
- # input string instead of 13th:)
35
- #--
36
- # $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
37
- #++
38
-
39
- require 'base64'
40
- require 'openssl'
41
- require 'openssl/digest'
42
- require 'socket'
43
-
44
- # Load Order is important here
45
- require 'net/ntlm/field'
46
- require 'net/ntlm/int16_le'
47
- require 'net/ntlm/int32_le'
48
- require 'net/ntlm/int64_le'
49
- require 'net/ntlm/string'
50
-
51
- require 'net/ntlm/field_set'
52
- require 'net/ntlm/blob'
53
- require 'net/ntlm/security_buffer'
54
- require 'net/ntlm/message'
55
- require 'net/ntlm/message/type0'
56
- require 'net/ntlm/message/type1'
57
- require 'net/ntlm/message/type2'
58
- require 'net/ntlm/message/type3'
59
-
60
- require 'net/ntlm/encode_util'
61
-
62
- require 'net/ntlm/client'
63
-
64
- module Net
65
- module NTLM
66
-
67
- LM_MAGIC = "KGS!@\#$%"
68
- TIME_OFFSET = 11644473600
69
- MAX64 = 0xffffffffffffffff
70
-
71
-
72
- class << self
73
-
74
- # Valid format for LAN Manager hex digest portion: 32 hexadecimal characters.
75
- LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
76
- # Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters.
77
- NT_LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
78
- # Valid format for an NTLM hash composed of `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`.
79
- DATA_REGEXP = /\A#{LAN_MANAGER_HEX_DIGEST_REGEXP}:#{NT_LAN_MANAGER_HEX_DIGEST_REGEXP}\z/
80
-
81
- # Takes a string and determines whether it is a valid NTLM Hash
82
- # @param [String] the string to validate
83
- # @return [Boolean] whether or not the string is a valid NTLM hash
84
- def is_ntlm_hash?(data)
85
- decoded_data = data.dup
86
- decoded_data = EncodeUtil.decode_utf16le(decoded_data)
87
- if DATA_REGEXP.match(decoded_data)
88
- true
89
- else
90
- false
91
- end
92
- end
93
-
94
- # Conver the value to a 64-Bit Little Endian Int
95
- # @param [String] val The string to convert
96
- def pack_int64le(val)
97
- [val & 0x00000000ffffffff, val >> 32].pack("V2")
98
- end
99
-
100
- # Builds an array of strings that are 7 characters long
101
- # @param [String] str The string to split
102
- # @api private
103
- def split7(str)
104
- s = str.dup
105
- until s.empty?
106
- (ret ||= []).push s.slice!(0, 7)
107
- end
108
- ret
109
- end
110
-
111
- # Not sure what this is doing
112
- # @param [String] str String to generate keys for
113
- # @api private
114
- def gen_keys(str)
115
- split7(str).map{ |str7|
116
- bits = split7(str7.unpack("B*")[0]).inject('')\
117
- {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
118
- [bits].pack("B*")
119
- }
120
- end
121
-
122
- def apply_des(plain, keys)
123
- dec = OpenSSL::Cipher::Cipher.new("des-cbc")
124
- dec.padding = 0
125
- keys.map {|k|
126
- dec.key = k
127
- dec.encrypt.update(plain) + dec.final
128
- }
129
- end
130
-
131
- # Generates a Lan Manager Hash
132
- # @param [String] password The password to base the hash on
133
- def lm_hash(password)
134
- keys = gen_keys password.upcase.ljust(14, "\0")
135
- apply_des(LM_MAGIC, keys).join
136
- end
137
-
138
- # Generate a NTLM Hash
139
- # @param [String] password The password to base the hash on
140
- # @option opt :unicode (false) Unicode encode the password
141
- def ntlm_hash(password, opt = {})
142
- pwd = password.dup
143
- unless opt[:unicode]
144
- pwd = EncodeUtil.encode_utf16le(pwd)
145
- end
146
- OpenSSL::Digest::MD4.digest pwd
147
- end
148
-
149
- # Generate a NTLMv2 Hash
150
- # @param [String] user The username
151
- # @param [String] password The password
152
- # @param [String] target The domain or workstation to authenticate to
153
- # @option opt :unicode (false) Unicode encode the domain
154
- def ntlmv2_hash(user, password, target, opt={})
155
- if is_ntlm_hash? password
156
- decoded_password = EncodeUtil.decode_utf16le(password)
157
- ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
158
- else
159
- ntlmhash = ntlm_hash(password, opt)
160
- end
161
- userdomain = user.upcase + target
162
- unless opt[:unicode]
163
- userdomain = EncodeUtil.encode_utf16le(userdomain)
164
- end
165
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
166
- end
167
-
168
- def lm_response(arg)
169
- begin
170
- hash = arg[:lm_hash]
171
- chal = arg[:challenge]
172
- rescue
173
- raise ArgumentError
174
- end
175
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
176
- keys = gen_keys hash.ljust(21, "\0")
177
- apply_des(chal, keys).join
178
- end
179
-
180
- def ntlm_response(arg)
181
- hash = arg[:ntlm_hash]
182
- chal = arg[:challenge]
183
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
184
- keys = gen_keys hash.ljust(21, "\0")
185
- apply_des(chal, keys).join
186
- end
187
-
188
- def ntlmv2_response(arg, opt = {})
189
- begin
190
- key = arg[:ntlmv2_hash]
191
- chal = arg[:challenge]
192
- ti = arg[:target_info]
193
- rescue
194
- raise ArgumentError
195
- end
196
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
197
-
198
- if opt[:client_challenge]
199
- cc = opt[:client_challenge]
200
- else
201
- cc = rand(MAX64)
202
- end
203
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
204
-
205
- if opt[:timestamp]
206
- ts = opt[:timestamp]
207
- else
208
- ts = Time.now.to_i
209
- end
210
- # epoch -> milsec from Jan 1, 1601
211
- ts = 10_000_000 * (ts + TIME_OFFSET)
212
-
213
- blob = Blob.new
214
- blob.timestamp = ts
215
- blob.challenge = cc
216
- blob.target_info = ti
217
-
218
- bb = blob.serialize
219
-
220
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
221
- end
222
-
223
- def lmv2_response(arg, opt = {})
224
- key = arg[:ntlmv2_hash]
225
- chal = arg[:challenge]
226
-
227
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
228
-
229
- if opt[:client_challenge]
230
- cc = opt[:client_challenge]
231
- else
232
- cc = rand(MAX64)
233
- end
234
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
235
-
236
- OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
237
- end
238
-
239
- def ntlm2_session(arg, opt = {})
240
- begin
241
- passwd_hash = arg[:ntlm_hash]
242
- chal = arg[:challenge]
243
- rescue
244
- raise ArgumentError
245
- end
246
- chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
247
-
248
- if opt[:client_challenge]
249
- cc = opt[:client_challenge]
250
- else
251
- cc = rand(MAX64)
252
- end
253
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
254
-
255
- keys = gen_keys(passwd_hash.ljust(21, "\0"))
256
- session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
257
- response = apply_des(session_hash, keys).join
258
- [cc.ljust(24, "\0"), response]
259
- end
260
- end
261
-
262
- end
263
- end
1
+ # encoding: UTF-8
2
+ #
3
+ # = net/ntlm.rb
4
+ #
5
+ # An NTLM Authentication Library for Ruby
6
+ #
7
+ # This code is a derivative of "dbf2.rb" written by yrock
8
+ # and Minero Aoki. You can find original code here:
9
+ # http://jp.rubyist.net/magazine/?0013-CodeReview
10
+ # -------------------------------------------------------------
11
+ # Copyright (c) 2005,2006 yrock
12
+ #
13
+ #
14
+ # 2006-02-11 refactored by Minero Aoki
15
+ # -------------------------------------------------------------
16
+ #
17
+ # All protocol information used to write this code stems from
18
+ # "The NTLM Authentication Protocol" by Eric Glass. The author
19
+ # would thank to him for this tremendous work and making it
20
+ # available on the net.
21
+ # http://davenport.sourceforge.net/ntlm.html
22
+ # -------------------------------------------------------------
23
+ # Copyright (c) 2003 Eric Glass
24
+ #
25
+ # -------------------------------------------------------------
26
+ #
27
+ # The author also looked Mozilla-Firefox-1.0.7 source code,
28
+ # namely, security/manager/ssl/src/nsNTLMAuthModule.cpp and
29
+ # Jonathan Bastien-Filiatrault's libntlm-ruby.
30
+ # "http://x2a.org/websvn/filedetails.php?
31
+ # repname=libntlm-ruby&path=%2Ftrunk%2Fntlm.rb&sc=1"
32
+ # The latter has a minor bug in its separate_keys function.
33
+ # The third key has to begin from the 14th character of the
34
+ # input string instead of 13th:)
35
+ #--
36
+ # $Id: ntlm.rb,v 1.1 2006/10/05 01:36:52 koheik Exp $
37
+ #++
38
+
39
+ require 'base64'
40
+ require 'openssl'
41
+ require 'openssl/digest'
42
+ require 'socket'
43
+
44
+ # Load Order is important here
45
+ require 'net/ntlm/exceptions'
46
+ require 'net/ntlm/field'
47
+ require 'net/ntlm/int16_le'
48
+ require 'net/ntlm/int32_le'
49
+ require 'net/ntlm/int64_le'
50
+ require 'net/ntlm/string'
51
+
52
+ require 'net/ntlm/field_set'
53
+ require 'net/ntlm/blob'
54
+ require 'net/ntlm/security_buffer'
55
+ require 'net/ntlm/message'
56
+ require 'net/ntlm/message/type0'
57
+ require 'net/ntlm/message/type1'
58
+ require 'net/ntlm/message/type2'
59
+ require 'net/ntlm/message/type3'
60
+
61
+ require 'net/ntlm/encode_util'
62
+
63
+ require 'net/ntlm/client'
64
+ require 'net/ntlm/channel_binding'
65
+ require 'net/ntlm/target_info'
66
+
67
+ module Net
68
+ module NTLM
69
+
70
+ LM_MAGIC = "KGS!@\#$%"
71
+ TIME_OFFSET = 11644473600
72
+ MAX64 = 0xffffffffffffffff
73
+
74
+
75
+ class << self
76
+
77
+ # Valid format for LAN Manager hex digest portion: 32 hexadecimal characters.
78
+ LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
79
+ # Valid format for NT LAN Manager hex digest portion: 32 hexadecimal characters.
80
+ NT_LAN_MANAGER_HEX_DIGEST_REGEXP = /[0-9a-f]{32}/i
81
+ # Valid format for an NTLM hash composed of `'<LAN Manager hex digest>:<NT LAN Manager hex digest>'`.
82
+ DATA_REGEXP = /\A#{LAN_MANAGER_HEX_DIGEST_REGEXP}:#{NT_LAN_MANAGER_HEX_DIGEST_REGEXP}\z/
83
+
84
+ # Takes a string and determines whether it is a valid NTLM Hash
85
+ # @param [String] the string to validate
86
+ # @return [Boolean] whether or not the string is a valid NTLM hash
87
+ def is_ntlm_hash?(data)
88
+ decoded_data = data.dup
89
+ decoded_data = EncodeUtil.decode_utf16le(decoded_data)
90
+ if DATA_REGEXP.match(decoded_data)
91
+ true
92
+ else
93
+ false
94
+ end
95
+ end
96
+
97
+ # Conver the value to a 64-Bit Little Endian Int
98
+ # @param [String] val The string to convert
99
+ def pack_int64le(val)
100
+ [val & 0x00000000ffffffff, val >> 32].pack("V2")
101
+ end
102
+
103
+ # Builds an array of strings that are 7 characters long
104
+ # @param [String] str The string to split
105
+ # @api private
106
+ def split7(str)
107
+ s = str.dup
108
+ until s.empty?
109
+ (ret ||= []).push s.slice!(0, 7)
110
+ end
111
+ ret
112
+ end
113
+
114
+ # Not sure what this is doing
115
+ # @param [String] str String to generate keys for
116
+ # @api private
117
+ def gen_keys(str)
118
+ split7(str).map{ |str7|
119
+ bits = split7(str7.unpack("B*")[0]).inject('')\
120
+ {|ret, tkn| ret += tkn + (tkn.gsub('1', '').size % 2).to_s }
121
+ [bits].pack("B*")
122
+ }
123
+ end
124
+
125
+ def apply_des(plain, keys)
126
+ dec = OpenSSL::Cipher::Cipher.new("des-cbc")
127
+ dec.padding = 0
128
+ keys.map {|k|
129
+ dec.key = k
130
+ dec.encrypt.update(plain) + dec.final
131
+ }
132
+ end
133
+
134
+ # Generates a Lan Manager Hash
135
+ # @param [String] password The password to base the hash on
136
+ def lm_hash(password)
137
+ keys = gen_keys password.upcase.ljust(14, "\0")
138
+ apply_des(LM_MAGIC, keys).join
139
+ end
140
+
141
+ # Generate a NTLM Hash
142
+ # @param [String] password The password to base the hash on
143
+ # @option opt :unicode (false) Unicode encode the password
144
+ def ntlm_hash(password, opt = {})
145
+ pwd = password.dup
146
+ unless opt[:unicode]
147
+ pwd = EncodeUtil.encode_utf16le(pwd)
148
+ end
149
+ OpenSSL::Digest::MD4.digest pwd
150
+ end
151
+
152
+ # Generate a NTLMv2 Hash
153
+ # @param [String] user The username
154
+ # @param [String] password The password
155
+ # @param [String] target The domain or workstation to authenticate to
156
+ # @option opt :unicode (false) Unicode encode the domain
157
+ def ntlmv2_hash(user, password, target, opt={})
158
+ if is_ntlm_hash? password
159
+ decoded_password = EncodeUtil.decode_utf16le(password)
160
+ ntlmhash = [decoded_password.upcase[33,65]].pack('H32')
161
+ else
162
+ ntlmhash = ntlm_hash(password, opt)
163
+ end
164
+ userdomain = user.upcase + target
165
+ unless opt[:unicode]
166
+ userdomain = EncodeUtil.encode_utf16le(userdomain)
167
+ end
168
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, ntlmhash, userdomain)
169
+ end
170
+
171
+ def lm_response(arg)
172
+ begin
173
+ hash = arg[:lm_hash]
174
+ chal = arg[:challenge]
175
+ rescue
176
+ raise ArgumentError
177
+ end
178
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
179
+ keys = gen_keys hash.ljust(21, "\0")
180
+ apply_des(chal, keys).join
181
+ end
182
+
183
+ def ntlm_response(arg)
184
+ hash = arg[:ntlm_hash]
185
+ chal = arg[:challenge]
186
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
187
+ keys = gen_keys hash.ljust(21, "\0")
188
+ apply_des(chal, keys).join
189
+ end
190
+
191
+ def ntlmv2_response(arg, opt = {})
192
+ begin
193
+ key = arg[:ntlmv2_hash]
194
+ chal = arg[:challenge]
195
+ ti = arg[:target_info]
196
+ rescue
197
+ raise ArgumentError
198
+ end
199
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
200
+
201
+ if opt[:client_challenge]
202
+ cc = opt[:client_challenge]
203
+ else
204
+ cc = rand(MAX64)
205
+ end
206
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
207
+
208
+ if opt[:timestamp]
209
+ ts = opt[:timestamp]
210
+ else
211
+ ts = Time.now.to_i
212
+ end
213
+ # epoch -> milsec from Jan 1, 1601
214
+ ts = 10_000_000 * (ts + TIME_OFFSET)
215
+
216
+ blob = Blob.new
217
+ blob.timestamp = ts
218
+ blob.challenge = cc
219
+ blob.target_info = ti
220
+
221
+ bb = blob.serialize
222
+
223
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + bb) + bb
224
+ end
225
+
226
+ def lmv2_response(arg, opt = {})
227
+ key = arg[:ntlmv2_hash]
228
+ chal = arg[:challenge]
229
+
230
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
231
+
232
+ if opt[:client_challenge]
233
+ cc = opt[:client_challenge]
234
+ else
235
+ cc = rand(MAX64)
236
+ end
237
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
238
+
239
+ OpenSSL::HMAC.digest(OpenSSL::Digest::MD5.new, key, chal + cc) + cc
240
+ end
241
+
242
+ def ntlm2_session(arg, opt = {})
243
+ begin
244
+ passwd_hash = arg[:ntlm_hash]
245
+ chal = arg[:challenge]
246
+ rescue
247
+ raise ArgumentError
248
+ end
249
+ chal = NTLM::pack_int64le(chal) if chal.is_a?(Integer)
250
+
251
+ if opt[:client_challenge]
252
+ cc = opt[:client_challenge]
253
+ else
254
+ cc = rand(MAX64)
255
+ end
256
+ cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
257
+
258
+ keys = gen_keys(passwd_hash.ljust(21, "\0"))
259
+ session_hash = OpenSSL::Digest::MD5.digest(chal + cc).slice(0, 8)
260
+ response = apply_des(session_hash, keys).join
261
+ [cc.ljust(24, "\0"), response]
262
+ end
263
+ end
264
+
265
+ end
266
+ end