Class: PacketGen::Plugin::ESP
- Inherits:
-
Header::Base
- Object
- Header::Base
- PacketGen::Plugin::ESP
- Includes:
- Crypto
- Defined in:
- lib/packetgen/plugin/esp.rb
Overview
A ESP header consists of:
-
a Security Parameters Index (##spi, Types::Int32 type),
-
a Sequence Number (#sn,
Int32
type), -
a #body (variable length),
-
an optional TFC padding (#tfc, variable length),
-
an optional #padding (to align ESP on 32-bit boundary, variable length),
-
a #pad_length (Types::Int8),
-
a Next header field (#next,
Int8
), -
and an optional Integrity Check Value (#icv, variable length).
Create an ESP header
# standalone
esp = PacketGen::Plugin::ESP.new
# in a packet
pkt = PacketGen.gen('IP').add('ESP')
# access to ESP header
pkt.esp # => PacketGen::Plugin::ESP
Examples
Create an enciphered UDP packet (ESP transport mode), using CBC mode
icmp = PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.2.1').
add('ESP', spi: 0xff456e01, sn: 12345678).
add('UDP', dport: 4567, sport: 45362, body 'abcdef')
cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.encrypt
cipher.key = 16bytes_key
iv = 16bytes_iv
esp.esp.encrypt! cipher, iv
Create a ESP packet tunneling a UDP one, using GCM combined mode
# create inner UDP packet
icmp = PacketGen.gen('IP', src: '192.168.1.1', dst: '192.168.2.1').
add('UDP', dport: 4567, sport: 45362, body 'abcdef')
# create outer ESP packet
esp = PacketGen.gen('IP', src '198.76.54.32', dst: '1.2.3.4').add('ESP')
esp.esp.spi = 0x87654321
esp.esp.sn = 0x123
esp.esp.icv_length = 16
# encapsulate ICMP packet in ESP one
esp.encapsulate icmp
# encrypt ESP payload
cipher = OpenSSL::Cipher.new('aes-128-gcm')
cipher.encrypt
cipher.key = 16bytes_key
iv = 8bytes_iv
esp.esp.encrypt! cipher, iv, salt: 4bytes_gcm_salt
Decrypt a ESP packet using CBC mode and HMAC-SHA-256
cipher = OpenSSL::Cipher.new('aes-128-cbc')
cipher.decrypt
cipher.key = 16bytes_key
hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
pkt.esp.decrypt! cipher, intmode: hmac # => true if ICV check OK
Constant Summary collapse
- IP_PROTOCOL =
IP protocol number for ESP
50
- UDP_PORT =
Well-known UDP port for ESP
4500
Instance Attribute Summary collapse
- #body ⇒ PacketGen::Types::String, PacketGen::Header::Base
-
#icv ⇒ PacketGen::Types::String, PacketGen::Header::Base
Integrity Check Value.
-
#icv_length ⇒ Integer
ICV (Integrity Check Value) length.
-
#next ⇒ Integer
8-bit next protocol value.
-
#pad_length ⇒ Integer
8-bit padding length.
-
#padding ⇒ PacketGen::Types::String, PacketGen::Header::Base
ESP padding.
-
#sn ⇒ Integer
32-bit Sequence Number.
-
#spi ⇒ Integer
32-bit Security Parameter Index.
-
#tfc ⇒ PacketGen::Types::String, PacketGen::Header::Base
Traffic Flow Confidentiality padding.
Instance Method Summary collapse
-
#decrypt!(cipher, options = {}) ⇒ Boolean
Decrypt in-place ESP payload and trailer.
-
#encrypt!(cipher, iv, options = {}) ⇒ self
Encrypt in-place ESP payload and trailer.
-
#initialize(options = {}) ⇒ ESP
constructor
A new instance of ESP.
-
#read(str) ⇒ self
Read a ESP packet from string.
Methods included from Crypto
#authenticate!, #authenticated?, #confidentiality_mode, #decipher, #encipher, #set_crypto
Constructor Details
#initialize(options = {}) ⇒ ESP
Returns a new instance of ESP.
126 127 128 129 |
# File 'lib/packetgen/plugin/esp.rb', line 126 def initialize(={}) @icv_length = [:icv_length] || 0 super end |
Instance Attribute Details
#body ⇒ PacketGen::Types::String, PacketGen::Header::Base
88 |
# File 'lib/packetgen/plugin/esp.rb', line 88 define_field :body, PacketGen::Types::String |
#icv ⇒ PacketGen::Types::String, PacketGen::Header::Base
Integrity Check Value
108 |
# File 'lib/packetgen/plugin/esp.rb', line 108 define_field :icv, PacketGen::Types::String |
#icv_length ⇒ Integer
ICV (Integrity Check Value) length
112 113 114 |
# File 'lib/packetgen/plugin/esp.rb', line 112 def icv_length @icv_length end |
#next ⇒ Integer
8-bit next protocol value
104 |
# File 'lib/packetgen/plugin/esp.rb', line 104 define_field :next, PacketGen::Types::Int8 |
#pad_length ⇒ Integer
8-bit padding length
100 |
# File 'lib/packetgen/plugin/esp.rb', line 100 define_field :pad_length, PacketGen::Types::Int8 |
#padding ⇒ PacketGen::Types::String, PacketGen::Header::Base
ESP padding
96 |
# File 'lib/packetgen/plugin/esp.rb', line 96 define_field :padding, PacketGen::Types::String |
#sn ⇒ Integer
32-bit Sequence Number
85 |
# File 'lib/packetgen/plugin/esp.rb', line 85 define_field :sn, PacketGen::Types::Int32 |
#spi ⇒ Integer
32-bit Security Parameter Index
81 |
# File 'lib/packetgen/plugin/esp.rb', line 81 define_field :spi, PacketGen::Types::Int32 |
#tfc ⇒ PacketGen::Types::String, PacketGen::Header::Base
Traffic Flow Confidentiality padding
92 |
# File 'lib/packetgen/plugin/esp.rb', line 92 define_field :tfc, PacketGen::Types::String |
Instance Method Details
#decrypt!(cipher, options = {}) ⇒ Boolean
Decrypt in-place ESP payload and trailer.
264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/packetgen/plugin/esp.rb', line 264 def decrypt!(cipher, ={}) opt = { salt: '', parse: true }.merge() set_crypto cipher, opt[:intmode] case confidentiality_mode when 'gcm' iv = self[:body].slice!(0, 8) real_iv = opt[:salt] + iv when 'cbc' cipher.padding = 0 real_iv = iv = self[:body].slice!(0, 16) when 'ctr' iv = self[:body].slice!(0, 8) real_iv = opt[:salt] + iv + [1].pack('N') else real_iv = iv = self[:body].slice!(0, 16) end cipher.iv = real_iv if authenticated? && (@icv_length.zero? || opt[:icv_length]) raise PacketGen::ParseError, 'unknown ICV size' unless opt[:icv_length] @icv_length = opt[:icv_length].to_i # reread ESP to handle new ICV size msg = self[:body].to_s + self[:pad_length].to_s msg += self[:next].to_s self[:icv].read msg.slice!(-@icv_length, @icv_length) self[:body].read msg[0..-3] self[:pad_length].read msg[-2] self[:next].read msg[-1] end authenticate_esp_header_if_needed , iv, icv private_decrypt opt end |
#encrypt!(cipher, iv, options = {}) ⇒ self
Encrypt in-place ESP payload and trailer.
This method removes all data from tfc
and padding
fields, as their enciphered values are concatenated into body
.
It also removes headers under ESP from packet, as they are enciphered in ESP body, and then are no more accessible.
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/packetgen/plugin/esp.rb', line 180 def encrypt!(cipher, iv, ={}) opt = { salt: '', tfc_size: 1444 }.merge() set_crypto cipher, opt[:intmode] real_iv = force_binary(opt[:salt]) + force_binary(iv) real_iv += [1].pack('N') if confidentiality_mode == 'ctr' cipher.iv = real_iv authenticate_esp_header_if_needed , iv case confidentiality_mode when 'cbc' cipher_len = self[:body].sz + 2 self.pad_length = (16 - (cipher_len % 16)) % 16 else mod4 = to_s.size % 4 self.pad_length = 4 - mod4 if mod4.positive? end if opt[:pad_length] self.pad_length = opt[:pad_length] padding = force_binary(opt[:padding] || (1..self.pad_length).to_a.pack('C*')) self[:padding].read padding else padding = force_binary(opt[:padding] || (1..self.pad_length).to_a.pack('C*')) self[:padding].read padding[0...self.pad_length] end tfc = '' if opt[:tfc] tfc_size = opt[:tfc_size] - self[:body].sz if tfc_size.positive? tfc_size = case confidentiality_mode when 'cbc' (tfc_size / 16) * 16 else (tfc_size / 4) * 4 end tfc = force_binary("\0" * tfc_size) end end msg = self[:body].to_s + tfc msg += self[:padding].to_s + self[:pad_length].to_s + self[:next].to_s enc_msg = encipher(msg) # as padding is used to pad for CBC mode, this is unused cipher.final self[:body] = PacketGen::Types::String.new.read(iv) self[:body] << enc_msg[0..-3] self[:pad_length].read enc_msg[-2] self[:next].read enc_msg[-1] # reset padding field as it has no sense in encrypted ESP self[:padding].read '' set_esp_icv_if_needed # Remove enciphered headers from packet id = header_id(self) if id < packet.headers.size - 1 (packet.headers.size - 1).downto(id + 1) do |index| packet.headers.delete_at index end end self end |
#read(str) ⇒ self
Read a ESP packet from string.
#padding and #tfc are not set as they are enciphered (impossible to guess their respective size). #pad_length and #next are also enciphered.
138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
# File 'lib/packetgen/plugin/esp.rb', line 138 def read(str) return self if str.nil? force_binary str self[:spi].read str[0, 4] self[:sn].read str[4, 4] self[:body].read str[8...-@icv_length - 2] self[:tfc].read '' self[:padding].read '' self[:pad_length].read str[-@icv_length - 2, 1] self[:next].read str[-@icv_length - 1, 1] self[:icv].read str[-@icv_length, @icv_length] if @icv_length self end |