Class: PacketGen::Plugin::ESP

Inherits:
Header::Base
  • Object
show all
Includes:
Crypto
Defined in:
lib/packetgen/plugin/esp.rb

Overview

A ESP header consists of:

  • a Security Parameters Index (##spi, BinStruct::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 (BinStruct::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

Author:

  • Sylvain Daubert

Constant Summary collapse

IP_PROTOCOL =

IP protocol number for ESP

50
UDP_PORT =

Well-known UDP port for ESP

4500

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Crypto

#authenticate!, #authenticated?, #compute_iv_for_decrypting, #compute_iv_for_encrypting, #confidentiality_mode, #decipher, #encipher, #set_crypto

Constructor Details

#initialize(options = {}) ⇒ ESP

Returns a new instance of ESP.

Parameters:

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :icv_length (Integer)

    ICV length

  • :spi (Integer)

    Security Parameters Index

  • :sn (Integer)

    Sequence Number

  • :body (::String)

    ESP payload data

  • :tfc (::String)

    Traffic Flow Confidentiality, random padding up to MTU

  • :padding (::String)

    ESP padding to align ESP on 32-bit boundary

  • :pad_length (Integer)

    padding length

  • :next (Integer)

    Next Header field

  • :icv (::String)

    Integrity Check Value



128
129
130
131
# File 'lib/packetgen/plugin/esp.rb', line 128

def initialize(options={})
  @icv_length = options[:icv_length] || 0
  super
end

Instance Attribute Details

#bodyBinStruct::String, PacketGen::Header::Base

Returns:

  • (BinStruct::String, PacketGen::Header::Base)


90
# File 'lib/packetgen/plugin/esp.rb', line 90

define_attr :body, BinStruct::String

#icvBinStruct::String, PacketGen::Header::Base

Integrity Check Value

Returns:

  • (BinStruct::String, PacketGen::Header::Base)


110
# File 'lib/packetgen/plugin/esp.rb', line 110

define_attr :icv, BinStruct::String

#icv_lengthInteger

ICV (Integrity Check Value) length

Returns:

  • (Integer)


114
115
116
# File 'lib/packetgen/plugin/esp.rb', line 114

def icv_length
  @icv_length
end

#nextInteger

8-bit next protocol value

Returns:

  • (Integer)


106
# File 'lib/packetgen/plugin/esp.rb', line 106

define_attr :next, BinStruct::Int8

#pad_lengthInteger

8-bit padding length

Returns:

  • (Integer)


102
# File 'lib/packetgen/plugin/esp.rb', line 102

define_attr :pad_length, BinStruct::Int8

#paddingBinStruct::String, PacketGen::Header::Base

ESP padding

Returns:

  • (BinStruct::String, PacketGen::Header::Base)


98
# File 'lib/packetgen/plugin/esp.rb', line 98

define_attr :padding, BinStruct::String

#snInteger

32-bit Sequence Number

Returns:

  • (Integer)


87
# File 'lib/packetgen/plugin/esp.rb', line 87

define_attr :sn, BinStruct::Int32

#spiInteger

32-bit Security Parameter Index

Returns:

  • (Integer)


83
# File 'lib/packetgen/plugin/esp.rb', line 83

define_attr :spi, BinStruct::Int32

#tfcBinStruct::String, PacketGen::Header::Base

Traffic Flow Confidentiality padding

Returns:

  • (BinStruct::String, PacketGen::Header::Base)


94
# File 'lib/packetgen/plugin/esp.rb', line 94

define_attr :tfc, BinStruct::String

Instance Method Details

#decrypt!(cipher, options = {}) ⇒ Boolean

Decrypt in-place ESP payload and trailer.

Parameters:

  • cipher (OpenSSL::Cipher)

    keyed cipher This cipher is confidentiality-only one, or AEAD one. To use a second cipher to add integrity, use :intmode option.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :parse (Boolean)

    parse deciphered payload to retrieve headers (default: true)

  • :icv_length (Fixnum)

    ICV length for captured packets, or read from PCapNG files

  • :salt (String)

    salt value for CTR and GCM modes

  • :esn (Fixnum)

    32 high-orber bits of ESN

  • :intmode (OpenSSL::HMAC)

    integrity mode to use with a confidentiality-only cipher. Only HMAC are supported.

Returns:

  • (Boolean)

    true if ESP packet is authenticated



213
214
215
216
217
218
219
220
221
222
223
224
# File 'lib/packetgen/plugin/esp.rb', line 213

def decrypt!(cipher, options={})
  opt = { salt: '', parse: true }.merge(options)

  set_crypto cipher, opt[:intmode]
  iv = compute_iv_for_decrypting(opt[:salt], self[:body])
  if authenticated? && (@icv_length.zero? || opt[:icv_length])
    check_icv_length(opt)
    decrypt_format_packet
  end
  authenticate_esp_header_if_needed options, 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.

Parameters:

  • cipher (OpenSSL::Cipher)

    keyed cipher. This cipher is confidentiality-only one, or AEAD one. To use a second cipher to add integrity, use :intmode option.

  • iv (String)

    full IV for encryption

    • CTR and GCM modes: iv is 8-bytes long.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :salt (String)

    salt value for CTR and GCM modes

  • :tfc (Boolean)
  • :tfc_size (Fixnum)

    ESP body size used for TFC (default 1444, max size for a tunneled IPv4/ESP packet). This is the maximum size for ESP packet (without IP header nor Eth one).

  • :esn (Fixnum)

    32 high-orber bits of ESN

  • :pad_length (Fixnum)

    set a padding length

  • :padding (String)

    set a padding. No check with :pad_length is made. If :pad_length is not set, :padding length is shortened to correct padding length

  • :intmode (OpenSSL::HMAC)

    integrity mode to use with a confidentiality-only cipher. Only HMAC are supported.

Returns:

  • (self)


181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/packetgen/plugin/esp.rb', line 181

def encrypt!(cipher, iv, options={}) # rubocop:disable Naming/MethodParameterName
  opt = { salt: '', tfc_size: 1444 }.merge(options)

  set_crypto cipher, opt[:intmode]
  compute_iv_for_encrypting iv, opt[:salt]

  authenticate_esp_header_if_needed options, iv

  encrypt_set_pad_length
  encrypt_set_padding(opt)
  encrypt_body(opt, iv)

  set_esp_icv_if_needed
  remove_enciphered_packets

  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.

Parameters:

  • str (String)

Returns:

  • (self)


140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/packetgen/plugin/esp.rb', line 140

def read(str)
  return self if str.nil?

  str = str.b
  self[:spi].read(str[0, 4])
  self[:sn].read(str[4, 4])
  self[:tfc].read('')
  self[:padding].read('')

  read_icv_dependent_fields(str[8..])
  read_icv(str)
  self
end