packetgen 2.8.7 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +0 -1
  3. data/README.md +5 -4
  4. data/lib/packetgen.rb +6 -12
  5. data/lib/packetgen/capture.rb +43 -39
  6. data/lib/packetgen/config.rb +0 -1
  7. data/lib/packetgen/deprecation.rb +1 -1
  8. data/lib/packetgen/header.rb +9 -9
  9. data/lib/packetgen/header/asn1_base.rb +10 -10
  10. data/lib/packetgen/header/base.rb +42 -101
  11. data/lib/packetgen/header/dhcp/option.rb +5 -11
  12. data/lib/packetgen/header/dhcpv6/duid.rb +2 -0
  13. data/lib/packetgen/header/dhcpv6/option.rb +2 -19
  14. data/lib/packetgen/header/dhcpv6/options.rb +7 -0
  15. data/lib/packetgen/header/dns.rb +5 -23
  16. data/lib/packetgen/header/dns/name.rb +1 -0
  17. data/lib/packetgen/header/dns/qdsection.rb +1 -0
  18. data/lib/packetgen/header/dns/question.rb +3 -7
  19. data/lib/packetgen/header/dns/rr.rb +3 -0
  20. data/lib/packetgen/header/dns/rrsection.rb +1 -0
  21. data/lib/packetgen/header/dot11.rb +1 -17
  22. data/lib/packetgen/header/dot1x.rb +1 -0
  23. data/lib/packetgen/header/eap.rb +4 -7
  24. data/lib/packetgen/header/eth.rb +2 -0
  25. data/lib/packetgen/header/http/headers.rb +3 -0
  26. data/lib/packetgen/header/http/request.rb +5 -4
  27. data/lib/packetgen/header/http/response.rb +5 -4
  28. data/lib/packetgen/header/icmp.rb +6 -0
  29. data/lib/packetgen/header/icmpv6.rb +6 -0
  30. data/lib/packetgen/header/igmpv3/mq.rb +2 -0
  31. data/lib/packetgen/header/ip.rb +32 -30
  32. data/lib/packetgen/header/ip/addr.rb +1 -0
  33. data/lib/packetgen/header/ip/option.rb +23 -20
  34. data/lib/packetgen/header/ip/options.rb +11 -24
  35. data/lib/packetgen/header/ipv6.rb +45 -34
  36. data/lib/packetgen/header/ipv6/addr.rb +2 -0
  37. data/lib/packetgen/header/ipv6/hop_by_hop.rb +7 -31
  38. data/lib/packetgen/header/mdns.rb +1 -0
  39. data/lib/packetgen/header/mldv2/mlq.rb +2 -0
  40. data/lib/packetgen/header/ospfv2/lsa.rb +15 -25
  41. data/lib/packetgen/header/ospfv3/ipv6_prefix.rb +1 -1
  42. data/lib/packetgen/header/ospfv3/lsa.rb +8 -25
  43. data/lib/packetgen/header/snmp.rb +2 -0
  44. data/lib/packetgen/header/tcp.rb +23 -2
  45. data/lib/packetgen/header/tcp/option.rb +51 -52
  46. data/lib/packetgen/header/tcp/options.rb +17 -52
  47. data/lib/packetgen/header/tftp.rb +3 -0
  48. data/lib/packetgen/header/udp.rb +8 -0
  49. data/lib/packetgen/packet.rb +119 -102
  50. data/lib/packetgen/pcapng/block.rb +4 -10
  51. data/lib/packetgen/pcapng/epb.rb +4 -4
  52. data/lib/packetgen/pcapng/file.rb +7 -3
  53. data/lib/packetgen/pcapng/idb.rb +2 -2
  54. data/lib/packetgen/pcapng/shb.rb +3 -3
  55. data/lib/packetgen/pcapng/spb.rb +1 -8
  56. data/lib/packetgen/pcapng/unknown_block.rb +0 -7
  57. data/lib/packetgen/types.rb +1 -0
  58. data/lib/packetgen/types/array.rb +73 -71
  59. data/lib/packetgen/types/cstring.rb +1 -1
  60. data/lib/packetgen/types/enum.rb +3 -3
  61. data/lib/packetgen/types/fields.rb +66 -106
  62. data/lib/packetgen/types/int.rb +9 -5
  63. data/lib/packetgen/types/length_from.rb +45 -0
  64. data/lib/packetgen/types/oui.rb +2 -0
  65. data/lib/packetgen/types/string.rb +10 -16
  66. data/lib/packetgen/types/tlv.rb +7 -15
  67. data/lib/packetgen/utils.rb +8 -8
  68. data/lib/packetgen/utils/arp_spoofer.rb +1 -2
  69. data/lib/packetgen/version.rb +1 -1
  70. metadata +3 -21
  71. data/lib/packetgen/header/crypto.rb +0 -62
  72. data/lib/packetgen/header/esp.rb +0 -413
  73. data/lib/packetgen/header/ike.rb +0 -243
  74. data/lib/packetgen/header/ike/auth.rb +0 -165
  75. data/lib/packetgen/header/ike/cert.rb +0 -76
  76. data/lib/packetgen/header/ike/certreq.rb +0 -66
  77. data/lib/packetgen/header/ike/id.rb +0 -99
  78. data/lib/packetgen/header/ike/ke.rb +0 -79
  79. data/lib/packetgen/header/ike/nonce.rb +0 -40
  80. data/lib/packetgen/header/ike/notify.rb +0 -176
  81. data/lib/packetgen/header/ike/payload.rb +0 -315
  82. data/lib/packetgen/header/ike/sa.rb +0 -561
  83. data/lib/packetgen/header/ike/sk.rb +0 -261
  84. data/lib/packetgen/header/ike/ts.rb +0 -270
  85. data/lib/packetgen/header/ike/vendor_id.rb +0 -39
  86. data/lib/packetgen/header/netbios.rb +0 -20
  87. data/lib/packetgen/header/netbios/datagram.rb +0 -105
  88. data/lib/packetgen/header/netbios/name.rb +0 -67
  89. data/lib/packetgen/header/netbios/session.rb +0 -64
@@ -38,23 +38,26 @@ module PacketGen
38
38
 
39
39
  # @abstract
40
40
  # Read an Int from a binary string or an integer
41
- # @param [Integer, String] value
41
+ # @param [Integer, #to_s] value
42
42
  # @return [self]
43
+ # @raise [ParseError] when reading +#to_s+ objects with abstract Int class.
43
44
  def read(value)
44
45
  @value = if value.is_a?(Integer)
45
46
  value.to_i
46
- else
47
+ elsif defined? @packstr
47
48
  value.to_s.unpack(@packstr[@endian]).first
49
+ else
50
+ raise ParseError, 'Int#read is abstract and cannot read'
48
51
  end
49
52
  self
50
53
  end
51
54
 
52
55
  # @abstract
53
56
  # @return [::String]
54
- # @raise [StandardError] This is an abstrat method and must be redefined
57
+ # @raise [ParseError] This is an abstrat method and must be redefined
55
58
  def to_s
56
59
  unless defined? @packstr
57
- raise StandardError, 'PacketGen::Types::Int#to_s is an abstract method'
60
+ raise ParseError, 'PacketGen::Types::Int#to_s is an abstract method'
58
61
  end
59
62
 
60
63
  [to_i].pack(@packstr[@endian])
@@ -66,6 +69,7 @@ module PacketGen
66
69
  @value || @default
67
70
  end
68
71
  alias to_human to_i
72
+ alias from_human value=
69
73
 
70
74
  # Convert Int to Float
71
75
  # @return [Float]
@@ -76,7 +80,7 @@ module PacketGen
76
80
  # Give size in bytes of self
77
81
  # @return [Integer]
78
82
  def sz
79
- to_s.size
83
+ width
80
84
  end
81
85
  end
82
86
 
@@ -0,0 +1,45 @@
1
+ # coding: utf-8
2
+ # This file is part of PacketGen
3
+ # See https://github.com/sdaubert/packetgen for more informations
4
+ # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
5
+ # This program is published under MIT license.
6
+
7
+ # frozen_string_literal: true
8
+
9
+ module PacketGen
10
+ module Types
11
+ # This module is a mixin adding +length_from+ capacity to a type.
12
+ # +length_from+ capacity is the capacity, for a type, to gets its
13
+ # length from another object.
14
+ # @author Sylvain Daubert
15
+ # @since 3.0.0
16
+ module LengthFrom
17
+ # Initialize +length from+ capacity.
18
+ # Should be call by extensed object's initialize.
19
+ # @param [Hash] options
20
+ # @option options [Types::Int,Proc] :length_from object or proc from which
21
+ # takes length when reading
22
+ # @return [void]
23
+ def initialize_length_from(options)
24
+ @length_from = options[:length_from]
25
+ end
26
+
27
+ # Return a substring from +str+ of length given in another object.
28
+ # @param [#to_s] str
29
+ # @return [String]
30
+ def read_with_length_from(str)
31
+ s = PacketGen.force_binary(str.to_s)
32
+ str_end = case @length_from
33
+ when Types::Int
34
+ @length_from.to_i
35
+ when Proc
36
+ @length_from.call
37
+ else
38
+ s.size
39
+ end
40
+ str_end = 0 if str_end < 0
41
+ s[0, str_end]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -29,8 +29,10 @@ module PacketGen
29
29
  # @return [OUI] self
30
30
  def from_human(str)
31
31
  return self if str.nil?
32
+
32
33
  bytes = str.split(/:/)
33
34
  raise ArgumentError, 'not a OUI' unless bytes.size == 3
35
+
34
36
  self[:b2].read(bytes[0].to_i(16))
35
37
  self[:b1].read(bytes[1].to_i(16))
36
38
  self[:b0].read(bytes[2].to_i(16))
@@ -12,39 +12,33 @@ module PacketGen
12
12
  # to be compatible with others {Types}.
13
13
  # @author Sylvain Daubert
14
14
  class String < ::String
15
+ include LengthFrom
16
+
17
+ # @return [Integer]
18
+ attr_reader :static_length
19
+
15
20
  # @param [Hash] options
16
21
  # @option options [Types::Int,Proc] :length_from object or proc from which
17
22
  # takes length when reading
18
23
  # @option options [Integer] :static_length set a static length for this string
19
24
  def initialize(options={})
20
25
  super()
21
- @length_from = options[:length_from]
26
+ initialize_length_from(options)
22
27
  @static_length = options[:static_length]
23
28
  end
24
29
 
25
30
  # @param [::String] str
26
31
  # @return [String] self
27
32
  def read(str)
28
- s = str.to_s
29
- str_end = case @length_from
30
- when Types::Int
31
- @length_from.to_i
32
- when Proc
33
- @length_from.call
34
- else
35
- if @static_length.is_a? Integer
36
- @static_length
37
- else
38
- s.size
39
- end
40
- end
41
- str_end = 0 if str_end < 0
42
- self.replace(s[0, str_end])
33
+ s = read_with_length_from(str)
34
+ s = s[0, static_length] if static_length
35
+ self.replace(s)
43
36
  self
44
37
  end
45
38
 
46
39
  alias sz length
47
40
  alias to_human to_s
41
+ alias from_human read
48
42
  end
49
43
  end
50
44
  end
@@ -75,6 +75,8 @@ module PacketGen
75
75
  # @private
76
76
  alias old_type= type=
77
77
 
78
+ undef type=, value=, value
79
+
78
80
  # Set type
79
81
  # @param [::String,Integer] val
80
82
  # @return [Integer]
@@ -86,8 +88,10 @@ module PacketGen
86
88
  self.old_type = val
87
89
  else
88
90
  raise TypeError, 'need an Integer' unless human_types?
91
+
89
92
  new_val = self.class::TYPES.key(val.to_s)
90
93
  raise ArgumentError, "unknown #{val} type" if new_val.nil?
94
+
91
95
  self.old_type = new_val
92
96
  end
93
97
  end
@@ -96,27 +100,15 @@ module PacketGen
96
100
  # @param [::String,Integer] val
97
101
  # @return [::String,Integer]
98
102
  def value=(val)
99
- if self[:value].respond_to? :from_human
100
- self[:value].from_human val
101
- elsif self[:value].is_a? Types::Int
102
- self[:value].value = val
103
- else
104
- self.length = val.length if val.is_a? ::String
105
- self[:value].read val
106
- end
103
+ self[:value].from_human val
104
+ self.length = self[:value].sz
107
105
  val
108
106
  end
109
107
 
110
108
  # Get +value+
111
109
  # @return [Object] depend on +value+ type
112
110
  def value
113
- if self[:value].respond_to? :to_human
114
- self[:value].to_human
115
- elsif self[:value].is_a? Types::Int
116
- self[:value].to_i
117
- else
118
- self[:value]
119
- end
111
+ self[:value].to_human
120
112
  end
121
113
 
122
114
  # Return human readable type, if TYPES is defined
@@ -42,6 +42,7 @@ module PacketGen
42
42
  # @option options [Integer] :timeout timeout in seconds before stopping
43
43
  # request. Default to 2.
44
44
  # @return [String,nil]
45
+ # @raise [RuntimeError] user don't have permission to capture packets on network device.
45
46
  def self.arp(ipaddr, options={})
46
47
  unless options[:no_cache]
47
48
  local_cache = self.arp_cache
@@ -65,6 +66,7 @@ module PacketGen
65
66
  cap_thread.join
66
67
 
67
68
  return if capture.packets.empty?
69
+
68
70
  capture.packets.each do |pkt|
69
71
  break pkt.arp.sha.to_s if pkt.arp.spa.to_s == ipaddr
70
72
  end
@@ -85,6 +87,7 @@ module PacketGen
85
87
  # @option options [String] :iface interface to use. Default to
86
88
  # {PacketGen.default_iface}
87
89
  # @return [void]
90
+ # @raise [RuntimeError] user don't have permission to capture packets on network device.
88
91
  def self.arp_spoof(target_ip, spoofed_ip, options={})
89
92
  interval = options[:interval] || 1.0
90
93
  as = ARPSpoofer.new(timeout: options[:for_seconds], interval: interval,
@@ -119,6 +122,7 @@ module PacketGen
119
122
  # pkt
120
123
  # end
121
124
  # @since 2.2.0
125
+ # @raise [RuntimeError] user don't have permission to capture packets on network device.
122
126
  def self.mitm(target1, target2, options={})
123
127
  options = { iface: PacketGen.default_iface }.merge(options)
124
128
 
@@ -144,14 +148,10 @@ module PacketGen
144
148
  iph = modified_pkt.ip
145
149
  l2 = modified_pkt.is?('Dot11') ? modified_pkt.dot11 : modified_pkt.eth
146
150
 
147
- if (iph.dst != my_ip) && (iph.src != my_ip)
148
- if (iph.src == target1) || (iph.dst == target2)
149
- l2.dst = mac2
150
- elsif (iph.src == target2) ||(iph.dst == target1)
151
- l2.dst = mac1
152
- else
153
- next
154
- end
151
+ if (iph.src == target1) || (iph.dst == target2)
152
+ l2.dst = mac2
153
+ elsif (iph.src == target2) || (iph.dst == target1)
154
+ l2.dst = mac1
155
155
  else
156
156
  next
157
157
  end
@@ -5,8 +5,6 @@
5
5
 
6
6
  # frozen_string_literal: true
7
7
 
8
- require 'thread'
9
-
10
8
  module PacketGen
11
9
  module Utils
12
10
  # @note This class is provided for test purpose.
@@ -117,6 +115,7 @@ module PacketGen
117
115
  # @return [Boolean,nil]
118
116
  def active?(target_ip)
119
117
  return unless @targets.key?(target_ip)
118
+
120
119
  @targets[target_ip][:active]
121
120
  end
122
121
 
@@ -10,5 +10,5 @@
10
10
  # @author Sylvain Daubert
11
11
  module PacketGen
12
12
  # PacketGen version
13
- VERSION = '2.8.7'
13
+ VERSION = '3.0.0'
14
14
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: packetgen
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.7
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-10-18 00:00:00.000000000 Z
11
+ date: 2018-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: interfacez
@@ -153,7 +153,6 @@ files:
153
153
  - lib/packetgen/header/asn1_base.rb
154
154
  - lib/packetgen/header/base.rb
155
155
  - lib/packetgen/header/bootp.rb
156
- - lib/packetgen/header/crypto.rb
157
156
  - lib/packetgen/header/dhcp.rb
158
157
  - lib/packetgen/header/dhcp/option.rb
159
158
  - lib/packetgen/header/dhcp/options.rb
@@ -183,7 +182,6 @@ files:
183
182
  - lib/packetgen/header/eap/md5.rb
184
183
  - lib/packetgen/header/eap/tls.rb
185
184
  - lib/packetgen/header/eap/ttls.rb
186
- - lib/packetgen/header/esp.rb
187
185
  - lib/packetgen/header/eth.rb
188
186
  - lib/packetgen/header/gre.rb
189
187
  - lib/packetgen/header/http.rb
@@ -198,19 +196,6 @@ files:
198
196
  - lib/packetgen/header/igmpv3/group_record.rb
199
197
  - lib/packetgen/header/igmpv3/mq.rb
200
198
  - lib/packetgen/header/igmpv3/mr.rb
201
- - lib/packetgen/header/ike.rb
202
- - lib/packetgen/header/ike/auth.rb
203
- - lib/packetgen/header/ike/cert.rb
204
- - lib/packetgen/header/ike/certreq.rb
205
- - lib/packetgen/header/ike/id.rb
206
- - lib/packetgen/header/ike/ke.rb
207
- - lib/packetgen/header/ike/nonce.rb
208
- - lib/packetgen/header/ike/notify.rb
209
- - lib/packetgen/header/ike/payload.rb
210
- - lib/packetgen/header/ike/sa.rb
211
- - lib/packetgen/header/ike/sk.rb
212
- - lib/packetgen/header/ike/ts.rb
213
- - lib/packetgen/header/ike/vendor_id.rb
214
199
  - lib/packetgen/header/ip.rb
215
200
  - lib/packetgen/header/ip/addr.rb
216
201
  - lib/packetgen/header/ip/option.rb
@@ -226,10 +211,6 @@ files:
226
211
  - lib/packetgen/header/mldv2/mcast_address_record.rb
227
212
  - lib/packetgen/header/mldv2/mlq.rb
228
213
  - lib/packetgen/header/mldv2/mlr.rb
229
- - lib/packetgen/header/netbios.rb
230
- - lib/packetgen/header/netbios/datagram.rb
231
- - lib/packetgen/header/netbios/name.rb
232
- - lib/packetgen/header/netbios/session.rb
233
214
  - lib/packetgen/header/ospfv2.rb
234
215
  - lib/packetgen/header/ospfv2/db_description.rb
235
216
  - lib/packetgen/header/ospfv2/hello.rb
@@ -271,6 +252,7 @@ files:
271
252
  - lib/packetgen/types/fields.rb
272
253
  - lib/packetgen/types/int.rb
273
254
  - lib/packetgen/types/int_string.rb
255
+ - lib/packetgen/types/length_from.rb
274
256
  - lib/packetgen/types/oui.rb
275
257
  - lib/packetgen/types/string.rb
276
258
  - lib/packetgen/types/tlv.rb
@@ -1,62 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module PacketGen
4
- module Header
5
- # Mixin for cryptographic classes
6
- # @api private
7
- # @author Sylvain Daubert
8
- module Crypto
9
- # Cryptographic error
10
- class Error < PacketGen::Error; end
11
-
12
- # Register cryptographic modes
13
- # @param [OpenSSL::Cipher] conf
14
- # @param [OpenSSL::HMAC] intg
15
- # @return [void]
16
- def set_crypto(conf, intg)
17
- @conf = conf
18
- @intg = intg
19
- return unless conf.authenticated?
20
- # #auth_tag_len only supported from ruby 2.4.0
21
- @conf.auth_tag_len = @trunc if @conf.respond_to? :auth_tag_len
22
- end
23
-
24
- # Get confidentiality mode name
25
- # @return [String]
26
- def confidentiality_mode
27
- mode = @conf.name.match(/-([^-]*)$/)[1]
28
- raise Error, 'unknown cipher mode' if mode.nil?
29
- mode.downcase
30
- end
31
-
32
- # Say if crypto modes permit authentication
33
- # @return [Boolean]
34
- def authenticated?
35
- @conf.authenticated? || !@intg.nil?
36
- end
37
-
38
- def authenticate!
39
- @conf.final
40
- if @intg
41
- @intg.update @esn.to_s if @esn
42
- @intg.digest[0, @icv_length] == @icv
43
- else
44
- true
45
- end
46
- rescue OpenSSL::Cipher::CipherError
47
- false
48
- end
49
-
50
- def encipher(data)
51
- enciphered_data = @conf.update(data)
52
- @intg.update(enciphered_data) if @intg
53
- enciphered_data
54
- end
55
-
56
- def decipher(data)
57
- @intg.update(data) if @intg
58
- @conf.update(data)
59
- end
60
- end
61
- end
62
- end
@@ -1,413 +0,0 @@
1
- # This file is part of PacketGen
2
- # See https://github.com/sdaubert/packetgen for more informations
3
- # Copyright (C) 2016 Sylvain Daubert <sylvain.daubert@laposte.net>
4
- # This program is published under MIT license.
5
-
6
- # frozen_string_literal: true
7
-
8
- module PacketGen
9
- module Header
10
- # A ESP header consists of:
11
- # * a Security Parameters Index (#{spi}, {Types::Int32} type),
12
- # * a Sequence Number ({#sn}, +Int32+ type),
13
- # * a {#body} (variable length),
14
- # * an optional TFC padding ({#tfc}, variable length),
15
- # * an optional {#padding} (to align ESP on 32-bit boundary, variable length),
16
- # * a {#pad_length} ({Types::Int8}),
17
- # * a Next header field ({#next}, +Int8+),
18
- # * and an optional Integrity Check Value ({#icv}, variable length).
19
- #
20
- # == Create an ESP header
21
- # # standalone
22
- # esp = PacketGen::Header::ESP.new
23
- # # in a packet
24
- # pkt = PacketGen.gen('IP').add('ESP')
25
- # # access to ESP header
26
- # pkt.esp # => PacketGen::Header::ESP
27
- #
28
- # == Examples
29
- # === Create an enciphered UDP packet (ESP transport mode), using CBC mode
30
- # icmp = PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.2.1').
31
- # add('ESP', spi: 0xff456e01, sn: 12345678).
32
- # add('UDP', dport: 4567, sport: 45362, body 'abcdef')
33
- # cipher = OpenSSL::Cipher.new('aes-128-cbc')
34
- # cipher.encrypt
35
- # cipher.key = 16bytes_key
36
- # iv = 16bytes_iv
37
- # esp.esp.encrypt! cipher, iv
38
- #
39
- # === Create a ESP packet tunneling a UDP one, using GCM combined mode
40
- # # create inner UDP packet
41
- # icmp = PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.2.1').
42
- # add('UDP', dport: 4567, sport: 45362, body 'abcdef')
43
- #
44
- # # create outer ESP packet
45
- # esp = PacketGen.gen('IP', src '198.76.54.32', dst: '1.2.3.4').add('ESP')
46
- # esp.esp.spi = 0x87654321
47
- # esp.esp.sn = 0x123
48
- # esp.esp.icv_length = 16
49
- # # encapsulate ICMP packet in ESP one
50
- # esp.encapsulate icmp
51
- #
52
- # # encrypt ESP payload
53
- # cipher = OpenSSL::Cipher.new('aes-128-gcm')
54
- # cipher.encrypt
55
- # cipher.key = 16bytes_key
56
- # iv = 8bytes_iv
57
- # esp.esp.encrypt! cipher, iv, salt: 4bytes_gcm_salt
58
- #
59
- # === Decrypt a ESP packet using CBC mode and HMAC-SHA-256
60
- # cipher = OpenSSL::Cipher.new('aes-128-cbc')
61
- # cipher.decrypt
62
- # cipher.key = 16bytes_key
63
- #
64
- # hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
65
- #
66
- # pkt.esp.decrypt! cipher, intmode: hmac # => true if ICV check OK
67
- # @author Sylvain Daubert
68
- # @since 1.2.0
69
- class ESP < Base
70
- include Crypto
71
-
72
- # IP protocol number for ESP
73
- IP_PROTOCOL = 50
74
-
75
- # Well-known UDP port for ESP
76
- UDP_PORT = 4500
77
-
78
- # @!attribute spi
79
- # 32-bit Security Parameter Index
80
- # @return [Integer]
81
- define_field :spi, Types::Int32
82
- # @!attribute sn
83
- # 32-bit Sequence Number
84
- # @return [Integer]
85
- define_field :sn, Types::Int32
86
- # @!attribute body
87
- # @return [Types::String,Header::Base]
88
- define_field :body, Types::String
89
- # @!attribute tfc
90
- # Traffic Flow Confidentiality padding
91
- # @return [Types::String,Header::Base]
92
- define_field :tfc, Types::String
93
- # @!attribute padding
94
- # ESP padding
95
- # @return [Types::String,Header::Base]
96
- define_field :padding, Types::String
97
- # @!attribute pad_length
98
- # 8-bit padding length
99
- # @return [Integer]
100
- define_field :pad_length, Types::Int8
101
- # @!attribute next
102
- # 8-bit next protocol value
103
- # @return [Integer]
104
- define_field :next, Types::Int8
105
- # @!attribute icv
106
- # Integrity Check Value
107
- # @return [Types::String,Header::Base]
108
- define_field :icv, Types::String
109
-
110
- # ICV (Integrity Check Value) length
111
- # @return [Integer]
112
- attr_accessor :icv_length
113
-
114
- # @param [Hash] options
115
- # @option options [Integer] :icv_length ICV length
116
- # @option options [Integer] :spi Security Parameters Index
117
- # @option options [Integer] :sn Sequence Number
118
- # @option options [::String] :body ESP payload data
119
- # @option options [::String] :tfc Traffic Flow Confidentiality, random padding
120
- # up to MTU
121
- # @option options [::String] :padding ESP padding to align ESP on 32-bit
122
- # boundary
123
- # @option options [Integer] :pad_length padding length
124
- # @option options [Integer] :next Next Header field
125
- # @option options [::String] :icv Integrity Check Value
126
- def initialize(options={})
127
- @icv_length = options[:icv_length] || 0
128
- super
129
- end
130
-
131
- # Read a ESP packet from string.
132
- #
133
- # {#padding} and {#tfc} are not set as they are enciphered (impossible
134
- # to guess their respective size). {#pad_length} and {#next} are also
135
- # enciphered.
136
- # @param [String] str
137
- # @return [self]
138
- def read(str)
139
- return self if str.nil?
140
- force_binary str
141
- self[:spi].read str[0, 4]
142
- self[:sn].read str[4, 4]
143
- self[:body].read str[8...-@icv_length - 2]
144
- self[:tfc].read ''
145
- self[:padding].read ''
146
- self[:pad_length].read str[-@icv_length - 2, 1]
147
- self[:next].read str[-@icv_length - 1, 1]
148
- self[:icv].read str[-@icv_length, @icv_length] if @icv_length
149
- self
150
- end
151
-
152
- # Encrypt in-place ESP payload and trailer.
153
- #
154
- # This method removes all data from +tfc+ and +padding+ fields, as their
155
- # enciphered values are concatenated into +body+.
156
- #
157
- # It also removes headers under ESP from packet, as they are enciphered in
158
- # ESP body, and then are no more accessible.
159
- # @param [OpenSSL::Cipher] cipher keyed cipher.
160
- # This cipher is confidentiality-only one, or AEAD one. To use a second
161
- # cipher to add integrity, use +:intmode+ option.
162
- # @param [String] iv full IV for encryption
163
- # * CTR and GCM modes: +iv+ is 8-bytes long.
164
- # @param [Hash] options
165
- # @option options [String] :salt salt value for CTR and GCM modes
166
- # @option options [Boolean] :tfc
167
- # @option options [Fixnum] :tfc_size ESP body size used for TFC
168
- # (default 1444, max size for a tunneled IPv4/ESP packet).
169
- # This is the maximum size for ESP packet (without IP header
170
- # nor Eth one).
171
- # @option options [Fixnum] :esn 32 high-orber bits of ESN
172
- # @option options [Fixnum] :pad_length set a padding length
173
- # @option options [String] :padding set a padding. No check with
174
- # +:pad_length+ is made. If +:pad_length+ is not set, +:padding+
175
- # length is shortened to correct padding length
176
- # @option options [OpenSSL::HMAC] :intmode integrity mode to use with a
177
- # confidentiality-only cipher. Only HMAC are supported.
178
- # @return [self]
179
- def encrypt!(cipher, iv, options={})
180
- opt = { salt: '', tfc_size: 1444 }.merge(options)
181
-
182
- set_crypto cipher, opt[:intmode]
183
-
184
- real_iv = force_binary(opt[:salt]) + force_binary(iv)
185
- real_iv += [1].pack('N') if confidentiality_mode == 'ctr'
186
- cipher.iv = real_iv
187
-
188
- authenticate_esp_header_if_needed options, iv
189
-
190
- case confidentiality_mode
191
- when 'cbc'
192
- cipher_len = self.body.sz + 2
193
- self.pad_length = (16 - (cipher_len % 16)) % 16
194
- else
195
- mod4 = to_s.size % 4
196
- self.pad_length = 4 - mod4 if mod4 > 0
197
- end
198
-
199
- if opt[:pad_length]
200
- self.pad_length = opt[:pad_length]
201
- padding = force_binary(opt[:padding] || (1..self.pad_length).to_a.pack('C*'))
202
- self[:padding].read padding
203
- else
204
- padding = force_binary(opt[:padding] || (1..self.pad_length).to_a.pack('C*'))
205
- self[:padding].read padding[0...self.pad_length]
206
- end
207
-
208
- tfc = ''
209
- if opt[:tfc]
210
- tfc_size = opt[:tfc_size] - body.sz
211
- if tfc_size > 0
212
- tfc_size = case confidentiality_mode
213
- when 'cbc'
214
- (tfc_size / 16) * 16
215
- else
216
- (tfc_size / 4) * 4
217
- end
218
- tfc = force_binary("\0" * tfc_size)
219
- end
220
- end
221
-
222
- msg = self.body.to_s + tfc
223
- msg += self[:padding].to_s + self[:pad_length].to_s + self[:next].to_s
224
- enc_msg = encipher(msg)
225
- # as padding is used to pad for CBC mode, this is unused
226
- cipher.final
227
-
228
- self[:body] = Types::String.new.read(iv) << enc_msg[0..-3]
229
- self[:pad_length].read enc_msg[-2]
230
- self[:next].read enc_msg[-1]
231
-
232
- # reset padding field as it has no sense in encrypted ESP
233
- self[:padding].read ''
234
-
235
- set_esp_icv_if_needed
236
-
237
- # Remove enciphered headers from packet
238
- id = header_id(self)
239
- if id < packet.headers.size - 1
240
- (packet.headers.size - 1).downto(id + 1) do |index|
241
- packet.headers.delete_at index
242
- end
243
- end
244
-
245
- self
246
- end
247
-
248
- # Decrypt in-place ESP payload and trailer.
249
- # @param [OpenSSL::Cipher] cipher keyed cipher
250
- # This cipher is confidentiality-only one, or AEAD one. To use a second
251
- # cipher to add integrity, use +:intmode+ option.
252
- # @param [Hash] options
253
- # @option options [Boolean] :parse parse deciphered payload to retrieve
254
- # headers (default: +true+)
255
- # @option options [Fixnum] :icv_length ICV length for captured packets,
256
- # or read from PCapNG files
257
- # @option options [String] :salt salt value for CTR and GCM modes
258
- # @option options [Fixnum] :esn 32 high-orber bits of ESN
259
- # @option options [OpenSSL::HMAC] :intmode integrity mode to use with a
260
- # confidentiality-only cipher. Only HMAC are supported.
261
- # @return [Boolean] +true+ if ESP packet is authenticated
262
- def decrypt!(cipher, options={})
263
- opt = { salt: '', parse: true }.merge(options)
264
-
265
- set_crypto cipher, opt[:intmode]
266
-
267
- case confidentiality_mode
268
- when 'gcm'
269
- iv = self.body.slice!(0, 8)
270
- real_iv = opt[:salt] + iv
271
- when 'cbc'
272
- cipher.padding = 0
273
- real_iv = iv = self.body.slice!(0, 16)
274
- when 'ctr'
275
- iv = self.body.slice!(0, 8)
276
- real_iv = opt[:salt] + iv + [1].pack('N')
277
- else
278
- real_iv = iv = self.body.slice!(0, 16)
279
- end
280
- cipher.iv = real_iv
281
-
282
- if authenticated? && (@icv_length.zero? || opt[:icv_length])
283
- raise ParseError, 'unknown ICV size' unless opt[:icv_length]
284
- @icv_length = opt[:icv_length].to_i
285
- # reread ESP to handle new ICV size
286
- msg = self.body.to_s + self[:pad_length].to_s
287
- msg += self[:next].to_s
288
- self[:icv].read msg.slice!(-@icv_length, @icv_length)
289
- self[:body].read msg[0..-3]
290
- self[:pad_length].read msg[-2]
291
- self[:next].read msg[-1]
292
- end
293
-
294
- authenticate_esp_header_if_needed options, iv, self[:icv]
295
- private_decrypt opt
296
- end
297
-
298
- private
299
-
300
- def get_auth_data(opt)
301
- ad = self[:spi].to_s
302
- if opt[:esn]
303
- @esn = Types::Int32.new(opt[:esn])
304
- ad << @esn.to_s if @conf.authenticated?
305
- end
306
- ad << self[:sn].to_s
307
- end
308
-
309
- def authenticate_esp_header_if_needed(opt, iv, icv=nil)
310
- if @conf.authenticated?
311
- @conf.auth_tag = icv if icv
312
- @conf.auth_data = get_auth_data(opt)
313
- elsif @intg
314
- @intg.reset
315
- @intg.update get_auth_data(opt)
316
- @intg.update iv
317
- @icv = icv
318
- else
319
- @icv = nil
320
- end
321
- end
322
-
323
- def set_esp_icv_if_needed
324
- return unless authenticated?
325
- if @conf.authenticated?
326
- self[:icv].read @conf.auth_tag[0, @icv_length]
327
- else
328
- self[:icv].read @intg.digest[0, @icv_length]
329
- end
330
- end
331
-
332
- def private_decrypt(options)
333
- # decrypt
334
- msg = self.body.to_s
335
- msg += self[:padding].to_s + self[:pad_length].to_s + self[:next].to_s
336
- plain_msg = decipher(msg)
337
-
338
- # check authentication tag
339
- if authenticated?
340
- return false unless authenticate!
341
- end
342
-
343
- # Set ESP fields
344
- self[:body].read plain_msg[0..-3]
345
- self[:pad_length].read plain_msg[-2]
346
- self[:next].read plain_msg[-1]
347
-
348
- # Set padding
349
- if self.pad_length > 0
350
- len = self.pad_length
351
- self[:padding].read self.body.slice!(-len, len)
352
- end
353
-
354
- # Set TFC padding
355
- encap_length = 0
356
- pkt = nil
357
- case self.next
358
- when 4 # IPv4
359
- pkt = Packet.parse(body, first_header: 'IP')
360
- encap_length = pkt.ip.length
361
- when 41 # IPv6
362
- pkt = Packet.parse(body, first_header: 'IPv6')
363
- encap_length = pkt.ipv6.length + pkt.ipv6.sz
364
- when ICMP::IP_PROTOCOL
365
- pkt = Packet.parse(body, first_header: 'ICMP')
366
- # no size field. cannot recover TFC padding
367
- encap_length = body.sz
368
- when UDP::IP_PROTOCOL
369
- pkt = Packet.parse(body, first_header: 'UDP')
370
- encap_length = pkt.udp.length
371
- when TCP::IP_PROTOCOL
372
- # No length in TCP header, so TFC may not be used.
373
- # Or underlayer protocol should have a size information...
374
- pkt = Packet.parse(body, first_header: 'TCP')
375
- encap_length = pkt.sz
376
- when ICMPv6::IP_PROTOCOL
377
- pkt = Packet.parse(body, first_header: 'ICMPv6')
378
- # no size field. cannot recover TFC padding
379
- encap_length = body.sz
380
- else
381
- # Unmanaged encapsulated protocol
382
- encap_length = body.sz
383
- end
384
-
385
- if encap_length < body.sz
386
- tfc_len = body.sz - encap_length
387
- self[:tfc].read self.body.slice!(encap_length, tfc_len)
388
- end
389
-
390
- if options[:parse]
391
- packet.encapsulate pkt unless pkt.nil?
392
- end
393
-
394
- true
395
- end
396
- end
397
-
398
- self.add_class ESP
399
-
400
- IP.bind ESP, protocol: ESP::IP_PROTOCOL
401
- IPv6.bind ESP, next: ESP::IP_PROTOCOL
402
- UDP.bind ESP, procs: [->(f) { f.dport = f.sport = ESP::UDP_PORT },
403
- ->(f) { (f.dport == ESP::UDP_PORT ||
404
- f.sport == ESP::UDP_PORT) &&
405
- Types::Int32.new.read(f.body[0..3]).to_i > 0 }]
406
- ESP.bind IP, next: 4
407
- ESP.bind IPv6, next: 41
408
- ESP.bind TCP, next: TCP::IP_PROTOCOL
409
- ESP.bind UDP, next: TCP::IP_PROTOCOL
410
- ESP.bind ICMP, next: ICMP::IP_PROTOCOL
411
- ESP.bind ICMPv6, next: ICMPv6::IP_PROTOCOL
412
- end
413
- end