Class: PacketGen::Header::ESP
- Extended by:
- HeaderClassMethods
- Includes:
- HeaderMethods, StructFu
- Defined in:
- lib/packetgen/header/esp.rb
Overview
A ESP header consists of:
-
a Security Parameters Index (##spi, StructFu::Int32 type),
-
a Sequence Number (#sn,
Int32type), -
a #body (variable length),
-
an optional TFC padding (#tfc, variable length),
-
an optional #padding (to align ESP on 32-bit boundary, variable length),
-
a Next header field (#next,
Int8), -
and an optional Integrity Check Value (#icv, variable length).
Create an ESP header
# standalone
esp = PacketGen::Header::ESP.new
# in a packet
pkt = PacketGen.gen('IP').add('ESP')
# access to ESP header
pkt.esp # => PacketGen::Header::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 ⇒ Object
Returns the value of attribute body.
-
#icv ⇒ Object
Returns the value of attribute icv.
-
#icv_length ⇒ Integer
ICV (Integrity Check Value) length.
-
#next ⇒ Integer
Getter for
nextattribute. -
#pad_length ⇒ Integer
Getter for
pad_lengthattribute. -
#padding ⇒ Object
Returns the value of attribute padding.
-
#sn ⇒ Integer
Getter for SN attribute.
-
#spi ⇒ Integer
Getter for SPI attribute.
-
#tfc ⇒ Object
Returns the value of attribute tfc.
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 HeaderClassMethods
bind_header, define_bit_fields_on, known_headers
Methods included from HeaderMethods
#header_id, #inspect, #ip_header, #packet, #packet=, #protocol_name
Methods included from StructFu
#clone, #set_endianness, #sz, #to_s, #typecast
Methods inherited from Struct
Constructor Details
#initialize(options = {}) ⇒ ESP
Returns a new instance of ESP.
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/packetgen/header/esp.rb', line 93 def initialize(={}) @icv_length = [:icv_length] || 0 super Int32.new([:spi]), Int32.new([:sn]), StructFu::String.new.read([:body]), StructFu::String.new.read([:tfc]), StructFu::String.new.read([:padding]), Int8.new([:pad_length]), Int8.new([:next]), StructFu::String.new.read([:icv]) end |
Instance Attribute Details
#body ⇒ Object
Returns the value of attribute body
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def body @body end |
#icv ⇒ Object
Returns the value of attribute icv
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def icv @icv end |
#icv_length ⇒ Integer
ICV (Integrity Check Value) length
79 80 81 |
# File 'lib/packetgen/header/esp.rb', line 79 def icv_length @icv_length end |
#next ⇒ Integer
Getter for next attribute
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def next @next end |
#pad_length ⇒ Integer
Getter for pad_length attribute
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def pad_length @pad_length end |
#padding ⇒ Object
Returns the value of attribute padding
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def padding @padding end |
#sn ⇒ Integer
Getter for SN attribute
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def sn @sn end |
#spi ⇒ Integer
Getter for SPI attribute
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def spi @spi end |
#tfc ⇒ Object
Returns the value of attribute tfc
65 66 67 |
# File 'lib/packetgen/header/esp.rb', line 65 def tfc @tfc end |
Instance Method Details
#decrypt!(cipher, options = {}) ⇒ Boolean
Decrypt in-place ESP payload and trailer.
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 |
# File 'lib/packetgen/header/esp.rb', line 289 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? and (@icv_length == 0 or opt[:icv_length]) raise 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, self[:icv] private_decrypt cipher, 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.
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 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
# File 'lib/packetgen/header/esp.rb', line 206 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 > 0 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] - body.sz if tfc_size > 0 case confidentiality_mode when 'cbc' tfc_size = (tfc_size / 16) * 16 else tfc_size = (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] = StructFu::String.new(iv) << 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.
112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/packetgen/header/esp.rb', line 112 def read(str) return self if str.nil? raise ParseError, 'string too short for ESP' if str.size < self.sz 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 |