packetgen-plugin-ipsec 1.0.0
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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.rubocop.yml +29 -0
- data/.travis.yml +12 -0
- data/Gemfile +3 -0
- data/LICENSE +21 -0
- data/README.md +97 -0
- data/Rakefile +13 -0
- data/lib/packetgen-plugin-ipsec.rb +4 -0
- data/lib/packetgen/plugin/crypto.rb +74 -0
- data/lib/packetgen/plugin/esp.rb +413 -0
- data/lib/packetgen/plugin/ike.rb +241 -0
- data/lib/packetgen/plugin/ike/auth.rb +165 -0
- data/lib/packetgen/plugin/ike/cert.rb +76 -0
- data/lib/packetgen/plugin/ike/certreq.rb +66 -0
- data/lib/packetgen/plugin/ike/id.rb +99 -0
- data/lib/packetgen/plugin/ike/ke.rb +79 -0
- data/lib/packetgen/plugin/ike/nonce.rb +40 -0
- data/lib/packetgen/plugin/ike/notify.rb +159 -0
- data/lib/packetgen/plugin/ike/payload.rb +304 -0
- data/lib/packetgen/plugin/ike/sa.rb +494 -0
- data/lib/packetgen/plugin/ike/sk.rb +261 -0
- data/lib/packetgen/plugin/ike/ts.rb +260 -0
- data/lib/packetgen/plugin/ike/vendor_id.rb +39 -0
- data/lib/packetgen/plugin/ipsec_version.rb +6 -0
- data/packetgen-plugin-ipsec.gemspec +29 -0
- metadata +152 -0
@@ -0,0 +1,241 @@
|
|
1
|
+
# This file is part of IPsec packetgen plugin.
|
2
|
+
# See https://github.com/sdaubert/packetgen-plugin-ipsec for more informations
|
3
|
+
# Copyright (c) 2018 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 Plugin
|
10
|
+
# This class handles a pseudo-Plugin used to differentiate ESP from IKE Plugins
|
11
|
+
# in a UDP datagram with port 4500.
|
12
|
+
# @author Sylvain Daubert
|
13
|
+
class NonESPMarker < PacketGen::Header::Base
|
14
|
+
# @!attribute non_esp_marker
|
15
|
+
# 32-bit zero marker to differentiate IKE packet over UDP port 4500 from ESP ones
|
16
|
+
# @return [Integer]
|
17
|
+
define_field :non_esp_marker, PacketGen::Types::Int32, default: 0
|
18
|
+
# @!attribute body
|
19
|
+
# @return [PacketGen::Types::String,PacketGen::Header::Base]
|
20
|
+
define_field :body, PacketGen::Types::String
|
21
|
+
|
22
|
+
# Check non_esp_marker field
|
23
|
+
# @see [PacketGen::Header::Base#parse?]
|
24
|
+
def parse?
|
25
|
+
non_esp_marker.zero?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# IKE is the Internet Key Exchange protocol (RFC 7296). Ony IKEv2 is supported.
|
30
|
+
#
|
31
|
+
# A IKE Plugin consists of a Plugin, and a set of payloads. This class
|
32
|
+
# handles IKE Plugin. For payloads, see {IKE::Payload}.
|
33
|
+
#
|
34
|
+
# == IKE Plugin
|
35
|
+
# The format of a IKE Plugin is shown below:
|
36
|
+
# 1 2 3
|
37
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
38
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
39
|
+
# | IKE SA Initiator's SPI |
|
40
|
+
# | |
|
41
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
42
|
+
# | IKE SA Responder's SPI |
|
43
|
+
# | |
|
44
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
45
|
+
# | Next Payload | MjVer | MnVer | Exchange Type | Flags |
|
46
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
47
|
+
# | Message ID |
|
48
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
49
|
+
# | Length |
|
50
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
51
|
+
# A IKE Plugin consists of:
|
52
|
+
# * a IKE SA initiator SPI ({#init_spi}, {PacketGen::Types::Int64} type),
|
53
|
+
# * a IKE SA responder SPI ({#resp_spi}, {PacketGen::Types::Int64} type),
|
54
|
+
# * a Next Payload field ({#next}, {PacketGen::Types::Int8} type),
|
55
|
+
# * a Version field ({#version}, {PacketGen::Types::Int8} type, with first 4-bit field
|
56
|
+
# as major number, and last 4-bit field as minor number),
|
57
|
+
# * a Exchange type ({#exchange_type}, {PacketGen::Types::Int8} type),
|
58
|
+
# * a {#flags} field ({PacketGen::Types::Int8} type),
|
59
|
+
# * a Message ID ({#message_id}, {PacketGen::Types::Int32} type),
|
60
|
+
# * and a {#length} ({PacketGen::Types::Int32} type).
|
61
|
+
#
|
62
|
+
# == Create a IKE Plugin
|
63
|
+
# === Standalone
|
64
|
+
# ike = PacketGen::Plugin::IKE.new
|
65
|
+
# === Classical IKE packet
|
66
|
+
# pkt = PacketGen.gen('IP').add('UDP').add('IKE')
|
67
|
+
# # access to IKE Plugin
|
68
|
+
# pkt.ike # => PacketGen::Plugin::IKE
|
69
|
+
# === NAT-T IKE packet
|
70
|
+
# # NonESPMarker is used to insert a 32-bit null field between UDP Plugin
|
71
|
+
# # and IKE one to differentiate it from ESP-in-UDP (see RFC 3948)
|
72
|
+
# pkt = PacketGen.gen('IP').add('UDP').add('NonESPMarker').add('IKE)
|
73
|
+
# @author Sylvain Daubert
|
74
|
+
class IKE < PacketGen::Header::Base
|
75
|
+
# Classical well-known UDP port for IKE
|
76
|
+
UDP_PORT1 = 500
|
77
|
+
# Well-known UDP port for IKE when NAT is detected
|
78
|
+
UDP_PORT2 = 4500
|
79
|
+
|
80
|
+
PROTOCOLS = {
|
81
|
+
'IKE' => 1,
|
82
|
+
'AH' => 2,
|
83
|
+
'ESP' => 3
|
84
|
+
}.freeze
|
85
|
+
|
86
|
+
EXCHANGE_TYPES = {
|
87
|
+
'IKE_SA_INIT' => 34,
|
88
|
+
'IKE_AUTH' => 35,
|
89
|
+
'CREATE_CHILD_SA' => 36,
|
90
|
+
'INFORMATIONAL' => 37
|
91
|
+
}.freeze
|
92
|
+
|
93
|
+
# @!attribute init_spi
|
94
|
+
# 64-bit initiator SPI
|
95
|
+
# @return [Integer]
|
96
|
+
define_field :init_spi, PacketGen::Types::Int64
|
97
|
+
# @!attribute resp_spi
|
98
|
+
# 64-bit responder SPI
|
99
|
+
# @return [Integer]
|
100
|
+
define_field :resp_spi, PacketGen::Types::Int64
|
101
|
+
# @!attribute next
|
102
|
+
# 8-bit next payload type
|
103
|
+
# @return [Integer]
|
104
|
+
define_field :next, PacketGen::Types::Int8
|
105
|
+
# @!attribute version
|
106
|
+
# 8-bit IKE version
|
107
|
+
# @return [Integer]
|
108
|
+
define_field :version, PacketGen::Types::Int8, default: 0x20
|
109
|
+
# @!attribute [r] exchange_type
|
110
|
+
# 8-bit exchange type
|
111
|
+
# @return [Integer]
|
112
|
+
define_field :exchange_type, PacketGen::Types::Int8Enum, enum: EXCHANGE_TYPES
|
113
|
+
# @!attribute flags
|
114
|
+
# 8-bit flags
|
115
|
+
# @return [Integer]
|
116
|
+
define_field :flags, PacketGen::Types::Int8
|
117
|
+
# @!attribute message_id
|
118
|
+
# 32-bit message ID
|
119
|
+
# @return [Integer]
|
120
|
+
define_field :message_id, PacketGen::Types::Int32
|
121
|
+
# @!attribute length
|
122
|
+
# 32-bit length of total message (Plugin + payloads)
|
123
|
+
# @return [Integer]
|
124
|
+
define_field :length, PacketGen::Types::Int32
|
125
|
+
|
126
|
+
# Defining a body permits using Packet#parse to parse IKE payloads.
|
127
|
+
# But this method is hidden as prefered way to access payloads is via #payloads
|
128
|
+
define_field :body, PacketGen::Types::String
|
129
|
+
|
130
|
+
# @!attribute mjver
|
131
|
+
# 4-bit major version value
|
132
|
+
# @return [Integer]
|
133
|
+
# @!attribute mnver
|
134
|
+
# 4-bit minor version value
|
135
|
+
# @return [Integer]
|
136
|
+
define_bit_fields_on :version, :mjver, 4, :mnver, 4
|
137
|
+
|
138
|
+
# @!attribute rsv1
|
139
|
+
# @return [Integer]
|
140
|
+
# @!attribute rsv2
|
141
|
+
# @return [Integer]
|
142
|
+
# @!attribute flag_i
|
143
|
+
# bit set in message sent by the original initiator
|
144
|
+
# @return [Boolean]
|
145
|
+
# @!attribute flag_r
|
146
|
+
# indicate this message is a response to a message containing the same Message ID
|
147
|
+
# @return [Boolean]
|
148
|
+
# @!attribute flag_v
|
149
|
+
# version flag. Ignored by IKEv2 peers, and should be set to 0
|
150
|
+
# @return [Boolean]
|
151
|
+
define_bit_fields_on :flags, :rsv1, 2, :flag_r, :flag_v, :flag_i, :rsv2, 3
|
152
|
+
|
153
|
+
# @param [Hash] options
|
154
|
+
# @see PacketGen::Header::Base#initialize
|
155
|
+
def initialize(options={})
|
156
|
+
super
|
157
|
+
calc_length unless options[:length]
|
158
|
+
self.type = options[:type] if options[:type]
|
159
|
+
self.type = options[:exchange_type] if options[:exchange_type]
|
160
|
+
end
|
161
|
+
|
162
|
+
alias type exchange_type
|
163
|
+
alias type= exchange_type=
|
164
|
+
|
165
|
+
# Get exchange type name
|
166
|
+
# @return [String
|
167
|
+
def human_exchange_type
|
168
|
+
self[:exchange_type].to_human
|
169
|
+
end
|
170
|
+
alias human_type human_exchange_type
|
171
|
+
|
172
|
+
# Calculate length field
|
173
|
+
# @return [Integer]
|
174
|
+
def calc_length
|
175
|
+
PacketGen::Header::Base.calculate_and_set_length self
|
176
|
+
end
|
177
|
+
|
178
|
+
# IKE payloads
|
179
|
+
# @return [Array<Payload>]
|
180
|
+
def payloads
|
181
|
+
payloads = []
|
182
|
+
body = self.body
|
183
|
+
while body.is_a?(Payload)
|
184
|
+
payloads << body
|
185
|
+
body = body.body
|
186
|
+
end
|
187
|
+
payloads
|
188
|
+
end
|
189
|
+
|
190
|
+
# @return [String]
|
191
|
+
def inspect
|
192
|
+
super do |attr|
|
193
|
+
case attr
|
194
|
+
when :flags
|
195
|
+
str_flags = ''.dup
|
196
|
+
%w[r v i].each do |flag|
|
197
|
+
str_flags << (send("flag_#{flag}?") ? flag.upcase : '.')
|
198
|
+
end
|
199
|
+
str = Inspect.shift_level
|
200
|
+
str << Inspect::FMT_ATTR % [self[attr].class.to_s.sub(/.*::/, ''), attr,
|
201
|
+
str_flags]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Toggle +I+ and +R+ flags.
|
207
|
+
# @return [self]
|
208
|
+
def reply!
|
209
|
+
self.flag_r = !self.flag_r?
|
210
|
+
self.flag_i = !self.flag_i?
|
211
|
+
self
|
212
|
+
end
|
213
|
+
|
214
|
+
# @api private
|
215
|
+
# @note This method is used internally by PacketGen and should not be
|
216
|
+
# directly called
|
217
|
+
# @param [Packet] packet
|
218
|
+
# @return [void]
|
219
|
+
def added_to_packet(packet)
|
220
|
+
return unless packet.is? 'UDP'
|
221
|
+
return unless packet.udp.sport.zero?
|
222
|
+
packet.udp.sport = if packet.is?('NonESPMarker')
|
223
|
+
UDP_PORT2
|
224
|
+
else
|
225
|
+
UDP_PORT1
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
Header.add_class IKE
|
231
|
+
Header.add_class NonESPMarker
|
232
|
+
|
233
|
+
PacketGen::Header::UDP.bind IKE, dport: IKE::UDP_PORT1
|
234
|
+
PacketGen::Header::UDP.bind IKE, sport: IKE::UDP_PORT1
|
235
|
+
PacketGen::Header::UDP.bind NonESPMarker, dport: IKE::UDP_PORT2
|
236
|
+
PacketGen::Header::UDP.bind NonESPMarker, sport: IKE::UDP_PORT2
|
237
|
+
NonESPMarker.bind IKE
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
require_relative 'ike/payload'
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# This file is part of IPsec packetgen plugin.
|
3
|
+
# See https://github.com/sdaubert/packetgen-plugin-ipsec for more informations
|
4
|
+
# Copyright (c) 2018 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 Plugin
|
11
|
+
class IKE
|
12
|
+
# This class handles Authentication payloads.
|
13
|
+
#
|
14
|
+
# A AUTH payload consists of the IKE generic payload Plugin (see {Payload})
|
15
|
+
# and some specific fields:
|
16
|
+
# 1 2 3
|
17
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
19
|
+
# | Next Payload |C| RESERVED | Payload Length |
|
20
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
21
|
+
# | Auth Method | RESERVED |
|
22
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
23
|
+
# | |
|
24
|
+
# ~ Authentication Data ~
|
25
|
+
# | |
|
26
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
27
|
+
# These specific fields are:
|
28
|
+
# * {#type} (ID type),
|
29
|
+
# * {#reserved},
|
30
|
+
# * and {#content} (Identification Data).
|
31
|
+
#
|
32
|
+
# == Create a KE payload
|
33
|
+
# # create a IKE packet with a Auth payload
|
34
|
+
# pkt = PacketGen.gen('IP').add('UDP').add('IKE').add('IKE::Auth', method: 'SHARED_KEY')
|
35
|
+
# pkt.calc_length
|
36
|
+
# @author Sylvain Daubert
|
37
|
+
class Auth < Payload
|
38
|
+
# Payload type number
|
39
|
+
PAYLOAD_TYPE = 39
|
40
|
+
|
41
|
+
METHODS = {
|
42
|
+
'RSA_SIGNATURE' => 1,
|
43
|
+
'SHARED_KEY' => 2,
|
44
|
+
'DSA_SIGNATURE' => 3,
|
45
|
+
'ECDSA256' => 9,
|
46
|
+
'ECDSA384' => 10,
|
47
|
+
'ECDSA512' => 11,
|
48
|
+
'PASSWORD' => 12,
|
49
|
+
'NULL' => 13,
|
50
|
+
'DIGITAL_SIGNATURE' => 14
|
51
|
+
}.freeze
|
52
|
+
|
53
|
+
# @attribute [r] method
|
54
|
+
# 8-bit Auth Method
|
55
|
+
# @return [Integer]
|
56
|
+
define_field_before :content, :method, PacketGen::Types::Int8Enum, enum: METHODS
|
57
|
+
# @attribute reserved
|
58
|
+
# 24-bit reserved field
|
59
|
+
# @return [Integer]
|
60
|
+
define_field_before :content, :reserved, PacketGen::Types::Int24
|
61
|
+
|
62
|
+
# Check authentication (see RFC 7296 §2.15)
|
63
|
+
# @param [Packet] init_msg first IKE message sent by peer
|
64
|
+
# @param [String] nonce my nonce, sent in first message
|
65
|
+
# @param [String] sk_p secret key used to compute prf(SK_px, IDx')
|
66
|
+
# @param [Integer] prf PRF type to use (see {Transform}+::PRF_*+ constants)
|
67
|
+
# @param [String] shared_secret shared secret to use as PSK (shared secret
|
68
|
+
# method only)
|
69
|
+
# @param [OpenSSL::X509::Certificate] cert certificate to check AUTH signature,
|
70
|
+
# if not embedded in IKE message
|
71
|
+
# @return [Boolean]
|
72
|
+
# @note For now, only NULL, SHARED_KEY and RSA, DSA and ECDSA signatures are
|
73
|
+
# supported.
|
74
|
+
# @note For certificates, only check AUTH authenticity with given (or guessed
|
75
|
+
# from packet) certificate, but certificate chain is not verified.
|
76
|
+
def check?(init_msg: nil, nonce: '', sk_p: '', prf: 1, shared_secret: '',
|
77
|
+
cert: nil)
|
78
|
+
raise TypeError, 'init_msg should be a Packet' unless init_msg.is_a?(Packet)
|
79
|
+
signed_octets = init_msg.ike.to_s
|
80
|
+
signed_octets << nonce
|
81
|
+
id = packet.ike.flag_i? ? packet.ike_idi : packet.ike_idr
|
82
|
+
signed_octets << prf(prf, sk_p, id.to_s[4, id.length - 4])
|
83
|
+
|
84
|
+
case method
|
85
|
+
when METHODS['SHARED_KEY']
|
86
|
+
auth = prf(prf(shared_secret, 'Key Pad for IKEv2'), signed_octets)
|
87
|
+
auth == content
|
88
|
+
when METHODS['RSA_SIGNATURE'], METHODS['ECDSA256'], METHODS['ECDSA384'],
|
89
|
+
METHODS['ECDSA512']
|
90
|
+
if packet.ike_cert
|
91
|
+
# FIXME: Expect a ENCODING_X509_CERT_SIG
|
92
|
+
# Others types not supported for now...
|
93
|
+
cert = OpenSSL::X509::Certificate.new(packet.ike_cert.content)
|
94
|
+
elsif cert.nil?
|
95
|
+
raise CryptoError, 'a certificate should be provided'
|
96
|
+
end
|
97
|
+
|
98
|
+
text = cert.to_text
|
99
|
+
m = text.match(/Public Key Algorithm: ([a-zA-Z0-9-]+)/)
|
100
|
+
digest = case m[1]
|
101
|
+
when 'id-ecPublicKey'
|
102
|
+
m2 = text.match(/Public-Key: \((\d+) bit\)/)
|
103
|
+
case m2[1]
|
104
|
+
when '256'
|
105
|
+
OpenSSL::Digest::SHA256.new
|
106
|
+
when '384'
|
107
|
+
OpenSSL::Digest::SHA384.new
|
108
|
+
when '521'
|
109
|
+
OpenSSL::Digest::SHA512.new
|
110
|
+
end
|
111
|
+
when /sha([235]\d+)/
|
112
|
+
OpenSSL::Digest.const_get("SHA#{$1}").new
|
113
|
+
when /sha1/, 'rsaEncryption'
|
114
|
+
OpenSSL::Digest::SHA1.new
|
115
|
+
end
|
116
|
+
signature = format_signature(cert.public_key, content.to_s)
|
117
|
+
cert.public_key.verify(digest, signature, signed_octets)
|
118
|
+
when METHOD_NULL
|
119
|
+
true
|
120
|
+
else
|
121
|
+
raise NotImplementedError, "unsupported method #{human_method}"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Get authentication method name
|
126
|
+
# @return [String]
|
127
|
+
def human_method
|
128
|
+
self[:method].to_human
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
|
133
|
+
def prf(type, key, msg)
|
134
|
+
case type
|
135
|
+
when Transform::PRF_HMAC_MD5, Transform::PRF_HMAC_SHA1,
|
136
|
+
Transform::PRF_HMAC_SHA2_256, Transform::PRF_HMAC_SHA2_384,
|
137
|
+
Transform::PRF_HMAC_SHA2_512
|
138
|
+
digestname = Transform.constants.grep(/PRF_/)
|
139
|
+
.detect { |c| Transform.const_get(c) == type }
|
140
|
+
.to_s.sub(/^PRF_HMAC_/, '').sub(/2_/, '')
|
141
|
+
digest = OpenSSL::Digest.const_get(digestname).new
|
142
|
+
else
|
143
|
+
raise NotImplementedError, 'for now, only HMAC-based PRF are supported'
|
144
|
+
end
|
145
|
+
hmac = OpenSSL::HMAC.new(key, digest)
|
146
|
+
hmac << msg
|
147
|
+
hmac.digest
|
148
|
+
end
|
149
|
+
|
150
|
+
def format_signature(pkey, sig)
|
151
|
+
if pkey.is_a?(OpenSSL::PKey::EC)
|
152
|
+
# PKey::EC need a signature as a DER string representing a sequence of
|
153
|
+
# 2 integers: r and s
|
154
|
+
r = OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(sig[0, sig.size / 2], 2).to_i)
|
155
|
+
s = OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(sig[sig.size / 2,
|
156
|
+
sig.size / 2], 2).to_i)
|
157
|
+
OpenSSL::ASN1::Sequence.new([r, s]).to_der
|
158
|
+
else
|
159
|
+
sig
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
# This file is part of IPsec packetgen plugin.
|
3
|
+
# See https://github.com/sdaubert/packetgen-plugin-ipsec for more informations
|
4
|
+
# Copyright (c) 2018 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 Plugin
|
11
|
+
class IKE
|
12
|
+
# This class handles Certificate payloads.
|
13
|
+
#
|
14
|
+
# A Cert payload consists of the IKE generic payload Plugin (see {Payload})
|
15
|
+
# and some specific fields:
|
16
|
+
# 1 2 3
|
17
|
+
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
18
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
19
|
+
# | Next Payload |C| RESERVED | Payload Length |
|
20
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
21
|
+
# | Cert Encoding | |
|
22
|
+
# +-+-+-+-+-+-+-+-+ +
|
23
|
+
# | |
|
24
|
+
# ~ Certificate Data ~
|
25
|
+
# | |
|
26
|
+
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
27
|
+
# These specific fields are:
|
28
|
+
# * {#encoding},
|
29
|
+
# * and {#content} (Certificate Data).
|
30
|
+
#
|
31
|
+
# == Create a Cert payload
|
32
|
+
# # Create a IKE packet with a Cert payload
|
33
|
+
# pkt = PacketGen.gen('IP').add('UDP').add('IKE').add('IKE::Cert', encoding: 'X509_CERT_SIG')
|
34
|
+
# certs = cert.to_der << ca_cert.to_der
|
35
|
+
# pkt.ike_cert.content.read certs
|
36
|
+
# pkt.calc_length
|
37
|
+
# @author Sylvain Daubert
|
38
|
+
class Cert < Payload
|
39
|
+
# Payload type number
|
40
|
+
PAYLOAD_TYPE = 37
|
41
|
+
|
42
|
+
ENCODINGS = {
|
43
|
+
'PKCS7_WRAPPED_X509' => 1,
|
44
|
+
'PGP' => 2,
|
45
|
+
'DNS_SIGNED_KEY' => 3,
|
46
|
+
'X509_CERT_SIG' => 4,
|
47
|
+
'KERBEROS_TOKEN' => 6,
|
48
|
+
'X509_CRL' => 7,
|
49
|
+
'X509_ARL' => 8,
|
50
|
+
'SPKI_CERT' => 9,
|
51
|
+
'X509_CERT_ATTR' => 10,
|
52
|
+
'HASH_URL_X509_CERT' => 12,
|
53
|
+
'HASH_URL_X509_BUNDLE' => 13
|
54
|
+
}.freeze
|
55
|
+
|
56
|
+
# @attribute encoding
|
57
|
+
# 8-bit certificate encoding
|
58
|
+
# @return [Integer]
|
59
|
+
define_field_before :content, :encoding, PacketGen::Types::Int8Enum, enum: ENCODINGS
|
60
|
+
|
61
|
+
def initialize(options={})
|
62
|
+
super
|
63
|
+
self.encoding = options[:encoding] if options[:encoding]
|
64
|
+
end
|
65
|
+
|
66
|
+
# Get encoding name
|
67
|
+
# @return [String]
|
68
|
+
def human_encoding
|
69
|
+
self[:encoding].to_human
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Header.add_class IKE::Cert
|
75
|
+
end
|
76
|
+
end
|