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,18 +1,18 @@
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 Type2 < Message
7
-
8
- string :sign, { :size => 8, :value => SSP_SIGN }
9
- int32LE :type, { :value => 2 }
10
- security_buffer :target_name, { :size => 0, :value => "" }
11
- int32LE :flag, { :value => DEFAULT_FLAGS[:TYPE2] }
12
- int64LE :challenge, { :value => 0}
13
- int64LE :context, { :value => 0, :active => false }
14
- security_buffer :target_info, { :value => "", :active => false }
15
- string :os_version, { :size => 8, :value => "", :active => false }
8
+ string :sign, { size: 8, value: SSP_SIGN }
9
+ int32_le :type, { value: 2 }
10
+ security_buffer :target_name, { size: 0, value: '' }
11
+ int32_le :flag, { value: DEFAULT_FLAGS[:TYPE2] }
12
+ int64_le :challenge, { value: 0 }
13
+ int64_le :context, { value: 0, active: false }
14
+ security_buffer :target_info, { value: '', active: false }
15
+ string :os_version, { size: 8, value: '', active: false }
16
16
 
17
17
  # Generates a Type 3 response based on the Type 2 Information
18
18
  # @return [Type3]
@@ -24,79 +24,99 @@ module Net
24
24
  # @note An empty :domain option authenticates to the local machine.
25
25
  # @note The :use_default_target has precedence over the :domain option
26
26
  def response(arg, opt = {})
27
+ creds = response_credentials(arg, opt)
28
+ normalize_response_encoding(creds, opt)
29
+ creds[:domain] = target_name if opt[:use_default_target]
30
+ lm_res, ntlm_res = build_responses(creds, opt)
31
+ build_type3(creds, lm_res, ntlm_res)
32
+ end
33
+
34
+ private
35
+
36
+ def response_credentials(arg, opt)
27
37
  usr = arg[:user]
28
38
  pwd = arg[:password]
29
- domain = arg[:domain] ? arg[:domain].upcase : ""
30
- if usr.nil? or pwd.nil?
31
- raise ArgumentError, "user and password have to be supplied"
32
- end
39
+ raise ArgumentError, 'user and password have to be supplied' if usr.nil? || pwd.nil?
33
40
 
34
- if opt[:workstation]
35
- ws = opt[:workstation]
36
- else
37
- ws = Socket.gethostname
38
- end
39
-
40
- if opt[:client_challenge]
41
- cc = opt[:client_challenge]
42
- else
43
- cc = rand(MAX64)
44
- end
45
- cc = NTLM::pack_int64le(cc) if cc.is_a?(Integer)
46
- opt[:client_challenge] = cc
41
+ build_credentials(usr, pwd, arg, opt)
42
+ end
47
43
 
48
- if has_flag?(:OEM) and opt[:unicode]
49
- usr = NTLM::EncodeUtil.decode_utf16le(usr)
50
- pwd = NTLM::EncodeUtil.decode_utf16le(pwd)
51
- ws = NTLM::EncodeUtil.decode_utf16le(ws)
52
- domain = NTLM::EncodeUtil.decode_utf16le(domain)
53
- opt[:unicode] = false
54
- end
44
+ def build_credentials(usr, pwd, arg, opt)
45
+ {
46
+ user: usr,
47
+ password: pwd,
48
+ domain: arg[:domain] ? arg[:domain].upcase : '',
49
+ workstation: opt[:workstation] || Socket.gethostname,
50
+ client_challenge: normalize_client_challenge(opt)
51
+ }
52
+ end
55
53
 
56
- if has_flag?(:UNICODE) and !opt[:unicode]
57
- usr = NTLM::EncodeUtil.encode_utf16le(usr)
58
- pwd = NTLM::EncodeUtil.encode_utf16le(pwd)
59
- ws = NTLM::EncodeUtil.encode_utf16le(ws)
60
- domain = NTLM::EncodeUtil.encode_utf16le(domain)
61
- opt[:unicode] = true
62
- end
54
+ def normalize_client_challenge(opt)
55
+ cc = opt[:client_challenge] || rand(MAX64)
56
+ cc = NTLM.pack_int64le(cc) if cc.is_a?(Integer)
57
+ opt[:client_challenge] = cc
58
+ end
63
59
 
64
- if opt[:use_default_target]
65
- domain = self.target_name
66
- end
60
+ def normalize_response_encoding(creds, opt)
61
+ decode_response_strings(creds, opt) if flag?(:OEM) && opt[:unicode]
62
+ encode_response_strings(creds, opt) if flag?(:UNICODE) && !opt[:unicode]
63
+ end
67
64
 
68
- ti = self.target_info
65
+ def decode_response_strings(creds, opt)
66
+ creds[:user] = NTLM::EncodeUtil.decode_utf16le(creds[:user])
67
+ creds[:password] = NTLM::EncodeUtil.decode_utf16le(creds[:password])
68
+ creds[:workstation] = NTLM::EncodeUtil.decode_utf16le(creds[:workstation])
69
+ creds[:domain] = NTLM::EncodeUtil.decode_utf16le(creds[:domain])
70
+ opt[:unicode] = false
71
+ end
69
72
 
70
- chal = self[:challenge].serialize
73
+ def encode_response_strings(creds, opt)
74
+ creds[:user] = NTLM::EncodeUtil.encode_utf16le(creds[:user])
75
+ creds[:password] = NTLM::EncodeUtil.encode_utf16le(creds[:password])
76
+ creds[:workstation] = NTLM::EncodeUtil.encode_utf16le(creds[:workstation])
77
+ creds[:domain] = NTLM::EncodeUtil.encode_utf16le(creds[:domain])
78
+ opt[:unicode] = true
79
+ end
71
80
 
81
+ def build_responses(creds, opt)
82
+ challenge = self[:challenge].serialize
72
83
  if opt[:ntlmv2]
73
- ar = {:ntlmv2_hash => NTLM::ntlmv2_hash(usr, pwd, domain, opt), :challenge => chal, :target_info => ti}
74
- lm_res = NTLM::lmv2_response(ar, opt)
75
- ntlm_res = NTLM::ntlmv2_response(ar, opt)
76
- elsif has_flag?(:NTLM2_KEY)
77
- ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
78
- lm_res, ntlm_res = NTLM::ntlm2_session(ar, opt)
84
+ ntlmv2_responses(creds, challenge, opt)
85
+ elsif flag?(:NTLM2_KEY)
86
+ ntlm2_session_responses(creds, challenge, opt)
79
87
  else
80
- ar = {:lm_hash => NTLM::lm_hash(pwd), :challenge => chal}
81
- lm_res = NTLM::lm_response(ar)
82
- ar = {:ntlm_hash => NTLM::ntlm_hash(pwd, opt), :challenge => chal}
83
- ntlm_res = NTLM::ntlm_response(ar)
88
+ v1_responses(creds, challenge, opt)
84
89
  end
90
+ end
85
91
 
86
- Type3.create({
87
- :lm_response => lm_res,
88
- :ntlm_response => ntlm_res,
89
- :domain => domain,
90
- :user => usr,
91
- :workstation => ws,
92
- :flag => self.flag
93
- })
92
+ def ntlmv2_responses(creds, challenge, opt)
93
+ ar = { ntlmv2_hash: NTLM.ntlmv2_hash(creds[:user], creds[:password], creds[:domain], opt),
94
+ challenge: challenge, target_info: target_info }
95
+ [NTLM.lmv2_response(ar, opt), NTLM.ntlmv2_response(ar, opt)]
94
96
  end
95
97
 
96
- end
98
+ def ntlm2_session_responses(creds, challenge, opt)
99
+ ar = { ntlm_hash: NTLM.ntlm_hash(creds[:password], opt), challenge: challenge }
100
+ NTLM.ntlm2_session(ar, opt)
101
+ end
102
+
103
+ def v1_responses(creds, challenge, opt)
104
+ lm_res = NTLM.lm_response({ lm_hash: NTLM.lm_hash(creds[:password]), challenge: challenge })
105
+ ntlm_res = NTLM.ntlm_response({ ntlm_hash: NTLM.ntlm_hash(creds[:password], opt), challenge: challenge })
106
+ [lm_res, ntlm_res]
107
+ end
97
108
 
109
+ def build_type3(creds, lm_res, ntlm_res)
110
+ Type3.create({
111
+ lm_response: lm_res,
112
+ ntlm_response: ntlm_res,
113
+ domain: creds[:domain],
114
+ user: creds[:user],
115
+ workstation: creds[:workstation],
116
+ flag: flag
117
+ })
118
+ end
119
+ end
98
120
  end
99
121
  end
100
122
  end
101
-
102
-
@@ -1,20 +1,20 @@
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 Type3 < Message
7
-
8
- string :sign, {:size => 8, :value => SSP_SIGN}
9
- int32LE :type, {:value => 3}
10
- security_buffer :lm_response, {:value => ""}
11
- security_buffer :ntlm_response, {:value => ""}
12
- security_buffer :domain, {:value => ""}
13
- security_buffer :user, {:value => ""}
14
- security_buffer :workstation, {:value => ""}
15
- security_buffer :session_key, {:value => "", :active => false }
16
- int32LE :flag, {:value => 0, :active => false }
17
- string :os_version, {:size => 8, :active => false }
8
+ string :sign, { size: 8, value: SSP_SIGN }
9
+ int32_le :type, { value: 3 }
10
+ security_buffer :lm_response, { value: '' }
11
+ security_buffer :ntlm_response, { value: '' }
12
+ security_buffer :domain, { value: '' }
13
+ security_buffer :user, { value: '' }
14
+ security_buffer :workstation, { value: '' }
15
+ security_buffer :session_key, { value: '', active: false }
16
+ int32_le :flag, { value: 0, active: false }
17
+ string :os_version, { size: 8, active: false }
18
18
 
19
19
  class << Type3
20
20
  # Builds a Type 3 packet
@@ -26,28 +26,33 @@ module Net
26
26
  # @option arg [String] :workstation The name of the calling workstation
27
27
  # @option arg [String] :session_key The session key
28
28
  # @option arg [Integer] :flag Flags for the packet
29
- def create(arg, opt ={})
29
+ def create(arg, _opt = {})
30
30
  t = new
31
31
  t.lm_response = arg[:lm_response]
32
32
  t.ntlm_response = arg[:ntlm_response]
33
33
  t.domain = arg[:domain]
34
34
  t.user = arg[:user]
35
+ t.workstation = arg[:workstation] if arg[:workstation]
36
+ enable_session_key(t, arg)
37
+ enable_flag(t, arg)
38
+ t
39
+ end
35
40
 
36
- if arg[:workstation]
37
- t.workstation = arg[:workstation]
38
- end
41
+ private
39
42
 
40
- if arg[:session_key]
41
- t.enable(:session_key)
42
- t.session_key = arg[:session_key]
43
- end
43
+ def enable_session_key(type3, arg)
44
+ return unless arg[:session_key]
44
45
 
45
- if arg[:flag]
46
- t.enable(:session_key)
47
- t.enable(:flag)
48
- t.flag = arg[:flag]
49
- end
50
- t
46
+ type3.enable(:session_key)
47
+ type3.session_key = arg[:session_key]
48
+ end
49
+
50
+ def enable_flag(type3, arg)
51
+ return unless arg[:flag]
52
+
53
+ type3.enable(:session_key)
54
+ type3.enable(:flag)
55
+ type3.flag = arg[:flag]
51
56
  end
52
57
  end
53
58
 
@@ -75,7 +80,7 @@ module Net
75
80
 
76
81
  # @return [Symbol]
77
82
  def ntlm_version
78
- if ntlm_response.size == 24 && lm_response[0,8] != "\x00"*8 && lm_response[8,16] == "\x00"*16
83
+ if ntlm_response.size == 24 && lm_response[0, 8] != "\x00" * 8 && lm_response[8, 16] == "\x00" * 16
79
84
  :ntlm2_session
80
85
  elsif ntlm_response.size == 24
81
86
  :ntlmv1
@@ -87,31 +92,28 @@ module Net
87
92
  private
88
93
 
89
94
  def ntlm2_session_password?(password, server_challenge)
90
- hash = ntlm_response
91
- _lm, empty_hash = NTLM.ntlm2_session(
92
- {
93
- :ntlm_hash => NTLM.ntlm_hash(password),
94
- :challenge => server_challenge,
95
- },
96
- {
97
- :client_challenge => lm_response[0,8]
98
- }
99
- )
100
- hash == empty_hash
95
+ ntlm_response == ntlm2_session_empty_hash(password, server_challenge)
96
+ end
97
+
98
+ def ntlm2_session_empty_hash(password, server_challenge)
99
+ NTLM.ntlm2_session(
100
+ { ntlm_hash: NTLM.ntlm_hash(password), challenge: server_challenge },
101
+ { client_challenge: lm_response[0, 8] }
102
+ ).last
101
103
  end
102
104
 
103
105
  def ntlmv2_password?(password, server_challenge)
104
106
  # user and domain are already UTF-16LE from the wire; encode the
105
107
  # supplied password to match before deriving the verification key.
106
- key = NTLM.ntlmv2_hash(user, EncodeUtil.encode_utf16le(password), domain, :unicode => true)
108
+ key = NTLM.ntlmv2_hash(user, EncodeUtil.encode_utf16le(password), domain, unicode: true)
107
109
  server_challenge = NTLM.pack_int64le(server_challenge) if server_challenge.is_a?(Integer)
108
110
 
109
111
  # Authenticate the exact blob bytes. Rebuilding the blob truncates
110
112
  # sub-second timestamps and rejects otherwise valid responses.
111
113
  expected_proof = OpenSSL::HMAC.digest(
112
- OpenSSL::Digest::MD5.new, key, server_challenge + ntlm_response[16..-1]
114
+ OpenSSL::Digest.new('MD5'), key, server_challenge + ntlm_response[16..]
113
115
  )
114
- expected_proof == ntlm_response[0,16]
116
+ expected_proof == ntlm_response[0, 16]
115
117
  end
116
118
  end
117
119
  end
@@ -1,137 +1,142 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Net
2
- module NTLM
3
-
4
- SSP_SIGN = "NTLMSSP\0"
5
-
6
- # See [2.2.2.5 NEGOTIATE](https://msdn.microsoft.com/en-us/library/cc236650.aspx)
7
- FLAGS = {
8
- :UNICODE => 0x00000001,
9
- :OEM => 0x00000002,
10
- :REQUEST_TARGET => 0x00000004,
11
- :SIGN => 0x00000010,
12
- :SEAL => 0x00000020,
13
- :NEG_DATAGRAM => 0x00000040,
14
- :NEG_LM_KEY => 0x00000080,
15
- :NTLM => 0x00000200,
16
- :NEG_ANONYMOUS => 0x00000800,
17
- :DOMAIN_SUPPLIED => 0x00001000,
18
- :WORKSTATION_SUPPLIED => 0x00002000,
19
- :ALWAYS_SIGN => 0x00008000,
20
- :TARGET_TYPE_DOMAIN => 0x00010000,
21
- :TARGET_TYPE_SERVER => 0x00020000,
22
- :NTLM2_KEY => 0x00080000,
23
- :NEG_IDENTIFY => 0x00100000,
24
- :NON_NT_SESSION_KEY => 0x00400000,
25
- :TARGET_INFO => 0x00800000,
26
- :NEG_VERSION => 0x02000000,
27
- :KEY128 => 0x20000000,
28
- :KEY_EXCHANGE => 0x40000000,
29
- :KEY56 => 0x80000000,
4
+ module NTLM
5
+ SSP_SIGN = "NTLMSSP\0"
6
+
7
+ # See [2.2.2.5 NEGOTIATE](https://msdn.microsoft.com/en-us/library/cc236650.aspx)
8
+ FLAGS = {
9
+ UNICODE: 0x00000001,
10
+ OEM: 0x00000002,
11
+ REQUEST_TARGET: 0x00000004,
12
+ SIGN: 0x00000010,
13
+ SEAL: 0x00000020,
14
+ NEG_DATAGRAM: 0x00000040,
15
+ NEG_LM_KEY: 0x00000080,
16
+ NTLM: 0x00000200,
17
+ NEG_ANONYMOUS: 0x00000800,
18
+ DOMAIN_SUPPLIED: 0x00001000,
19
+ WORKSTATION_SUPPLIED: 0x00002000,
20
+ ALWAYS_SIGN: 0x00008000,
21
+ TARGET_TYPE_DOMAIN: 0x00010000,
22
+ TARGET_TYPE_SERVER: 0x00020000,
23
+ NTLM2_KEY: 0x00080000,
24
+ NEG_IDENTIFY: 0x00100000,
25
+ NON_NT_SESSION_KEY: 0x00400000,
26
+ TARGET_INFO: 0x00800000,
27
+ NEG_VERSION: 0x02000000,
28
+ KEY128: 0x20000000,
29
+ KEY_EXCHANGE: 0x40000000,
30
+ KEY56: 0x80000000,
30
31
  # Undocumented flags:
31
- :MBZ9 => 0x00000008,
32
- :NETWARE => 0x00000100,
33
- :NEG_NT_ONLY => 0x00000400,
34
- :MBZ7 => 0x00000800, # alias for :NEG_ANONYMOUS
35
- :LOCAL_CALL => 0x00004000,
36
- }.freeze
37
-
38
- FLAG_KEYS = FLAGS.keys.sort{|a, b| FLAGS[a] <=> FLAGS[b] }
39
-
40
- DEFAULT_FLAGS = {
41
- :TYPE1 => FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY],
42
- :TYPE2 => FLAGS[:UNICODE],
43
- :TYPE3 => FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
44
- }
45
-
46
- # @private false
47
- class Message < FieldSet
48
- class << Message
32
+ MBZ9: 0x00000008,
33
+ NETWARE: 0x00000100,
34
+ NEG_NT_ONLY: 0x00000400,
35
+ MBZ7: 0x00000800, # alias for :NEG_ANONYMOUS
36
+ LOCAL_CALL: 0x00004000
37
+ }.freeze
38
+
39
+ FLAG_KEYS = FLAGS.keys.sort { |a, b| FLAGS[a] <=> FLAGS[b] }.freeze
40
+
41
+ DEFAULT_FLAGS = {
42
+ TYPE1: FLAGS[:UNICODE] | FLAGS[:OEM] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] |
43
+ FLAGS[:NTLM2_KEY],
44
+ TYPE2: FLAGS[:UNICODE],
45
+ TYPE3: FLAGS[:UNICODE] | FLAGS[:REQUEST_TARGET] | FLAGS[:NTLM] | FLAGS[:ALWAYS_SIGN] | FLAGS[:NTLM2_KEY]
46
+ }.freeze
47
+
48
+ # @private false
49
+ class Message < FieldSet
50
+ class << Message
51
+ def parse(str)
52
+ m = Type0.new
53
+ m.parse(str)
54
+ type_class = { 1 => Type1, 2 => Type2, 3 => Type3 }[m.type]
55
+ raise ArgumentError, "unknown type: #{m.type}" unless type_class
56
+
57
+ type_class.new.parse(str)
58
+ end
59
+
60
+ def decode64(str)
61
+ parse(Base64.decode64(str))
62
+ end
63
+ end
64
+
65
+ # @return [self]
49
66
  def parse(str)
50
- m = Type0.new
51
- m.parse(str)
52
- case m.type
53
- when 1
54
- t = Type1.new.parse(str)
55
- when 2
56
- t = Type2.new.parse(str)
57
- when 3
58
- t = Type3.new.parse(str)
59
- else
60
- raise ArgumentError, "unknown type: #{m.type}"
67
+ super
68
+
69
+ while disabled_fields? && serialize.size < str.size
70
+ enable_next_disabled_field
71
+ super
61
72
  end
62
- t
73
+
74
+ self
63
75
  end
64
76
 
65
- def decode64(str)
66
- parse(Base64.decode64(str))
77
+ def flag?(flag)
78
+ (self[:flag].value & FLAGS[flag]) == FLAGS[flag]
67
79
  end
68
- end
69
80
 
70
- # @return [self]
71
- def parse(str)
72
- super
81
+ alias has_flag? flag?
73
82
 
74
- while has_disabled_fields? && serialize.size < str.size
75
- # enable the next disabled field
76
- self.class.names.find { |name| !self[name].active && enable(name) }
77
- super
83
+ def flag=(flag)
84
+ self[:flag].value |= FLAGS[flag]
78
85
  end
79
86
 
80
- self
81
- end
82
-
83
- def has_flag?(flag)
84
- (self[:flag].value & FLAGS[flag]) == FLAGS[flag]
85
- end
87
+ alias set_flag flag=
86
88
 
87
- def set_flag(flag)
88
- self[:flag].value |= FLAGS[flag]
89
- end
89
+ def dump_flags
90
+ FLAG_KEYS.each { |k| print(k, '=', flag?(k), "\n") }
91
+ end
90
92
 
91
- def dump_flags
92
- FLAG_KEYS.each{ |k| print(k, "=", has_flag?(k), "\n") }
93
- end
93
+ def serialize
94
+ deflag
95
+ super + security_buffers.map { |_n, f|
96
+ f.value + (flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '')
97
+ }.join
98
+ end
94
99
 
95
- def serialize
96
- deflag
97
- super + security_buffers.map{|n, f| f.value + (has_flag?(:UNICODE) ? "\x00".b * (f.value.length % 2) : '')}.join
98
- end
100
+ def encode64
101
+ Base64.encode64(serialize).gsub(/\n/, '')
102
+ end
99
103
 
100
- def encode64
101
- Base64.encode64(serialize).gsub(/\n/, '')
102
- end
104
+ def decode64(str)
105
+ parse(Base64.decode64(str))
106
+ end
103
107
 
104
- def decode64(str)
105
- parse(Base64.decode64(str))
106
- end
108
+ alias head_size size
107
109
 
108
- alias head_size size
110
+ def data_size
111
+ security_buffers.inject(0) { |sum, a| sum + a[1].data_size }
112
+ end
109
113
 
110
- def data_size
111
- security_buffers.inject(0){|sum, a| sum += a[1].data_size}
112
- end
114
+ def size
115
+ head_size + data_size
116
+ end
113
117
 
114
- def size
115
- head_size + data_size
116
- end
118
+ def security_buffers
119
+ @alist.find_all { |_n, f| f.instance_of?(SecurityBuffer) }
120
+ end
117
121
 
122
+ def deflag
123
+ security_buffers.inject(head_size) { |cur, a|
124
+ a[1].offset = cur
125
+ cur += a[1].data_size
126
+ flag?(:UNICODE) ? cur + cur % 2 : cur
127
+ }
128
+ end
118
129
 
119
- def security_buffers
120
- @alist.find_all{|n, f| f.instance_of?(SecurityBuffer)}
121
- end
130
+ def data_edge
131
+ security_buffers.map { |_n, f| f.active ? f.offset : size }.min
132
+ end
122
133
 
123
- def deflag
124
- security_buffers.inject(head_size){|cur, a|
125
- a[1].offset = cur
126
- cur += a[1].data_size
127
- has_flag?(:UNICODE) ? cur + cur % 2 : cur
128
- }
129
- end
134
+ private
130
135
 
131
- def data_edge
132
- security_buffers.map{ |n, f| f.active ? f.offset : size}.min
136
+ # enable the next disabled field
137
+ def enable_next_disabled_field
138
+ self.class.names.find { |name| !self[name].active && enable(name) }
139
+ end
133
140
  end
134
-
135
141
  end
136
142
  end
137
- end