packetgen 1.4.3 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -248,13 +248,13 @@ module PacketGen
248
248
  flags = [:qr, :aa, :tc, :rd, :ra].select! { |attr| send "#{attr}?" }.
249
249
  map(&:to_s).join(',')
250
250
  str << Inspect.shift_level(2)
251
- str << Inspect::INSPECT_FMT_ATTR % ['Flags', 'flags', flags]
251
+ str << Inspect::FMT_ATTR % ['Flags', 'flags', flags]
252
252
  opcode = '%-10s (%u)' % [OPCODES.key(self.opcode), self.opcode]
253
253
  str << Inspect.shift_level(2)
254
- str << Inspect::INSPECT_FMT_ATTR % ['Integer', 'opcode', opcode]
254
+ str << Inspect::FMT_ATTR % ['Integer', 'opcode', opcode]
255
255
  rcode = '%-10s (%u)' % [RCODES.key(self.rcode), self.rcode]
256
256
  str << Inspect.shift_level(2)
257
- str << Inspect::INSPECT_FMT_ATTR % ['Integer', 'rcode', rcode]
257
+ str << Inspect::FMT_ATTR % ['Integer', 'rcode', rcode]
258
258
  else
259
259
  str << Inspect.inspect_attribute(attr, value, 2)
260
260
  end
@@ -133,6 +133,11 @@ module PacketGen
133
133
  # * a {#ht_ctrl} ({Types::Int32}),
134
134
  # * a {#body} (a {Types::String} or another {Base} class),
135
135
  # * a Frame check sequence ({#fcs}, of type {Types::Int32le})
136
+ #
137
+ # == header accessors
138
+ # As Dot11 header types are defined under Dot11 namespace, Dot11 header accessors
139
+ # have a specific name. By example, to access to a {Dot11::Beacon} header,
140
+ # accessor is +#dot11_beacon+.
136
141
  # @author Sylvain Daubert
137
142
  class Dot11 < Base
138
143
 
@@ -1,9 +1,6 @@
1
1
  module PacketGen
2
2
  module Header
3
3
 
4
- # Error about enciphering/deciphering was encountered
5
- class CipherError < Error;end
6
-
7
4
  # A ESP header consists of:
8
5
  # * a Security Parameters Index (#{spi}, {Types::Int32} type),
9
6
  # * a Sequence Number ({#sn}, +Int32+ type),
@@ -48,14 +45,14 @@ module PacketGen
48
45
  #
49
46
  # # encrypt ESP payload
50
47
  # cipher = OpenSSL::Cipher.new('aes-128-gcm')
51
- # cipher.encrypt!
48
+ # cipher.encrypt
52
49
  # cipher.key = 16bytes_key
53
50
  # iv = 8bytes_iv
54
51
  # esp.esp.encrypt! cipher, iv, salt: 4bytes_gcm_salt
55
52
  #
56
53
  # === Decrypt a ESP packet using CBC mode and HMAC-SHA-256
57
54
  # cipher = OpenSSL::Cipher.new('aes-128-cbc')
58
- # cipher.decrypt!
55
+ # cipher.decrypt
59
56
  # cipher.key = 16bytes_key
60
57
  #
61
58
  # hmac = OpenSSL::HMAC.new(hmac_key, OpenSSL::Digest::SHA256.new)
@@ -63,6 +60,7 @@ module PacketGen
63
60
  # pkt.esp.decrypt! cipher, intmode: hmac # => true if ICV check OK
64
61
  # @author Sylvain Daubert
65
62
  class ESP < Base
63
+ include Crypto
66
64
 
67
65
  # IP protocol number for ESP
68
66
  IP_PROTOCOL = 50
@@ -292,43 +290,6 @@ module PacketGen
292
290
 
293
291
  private
294
292
 
295
- def set_crypto(conf, intg)
296
- @conf, @intg = conf, intg
297
- end
298
-
299
- def confidentiality_mode
300
- mode = @conf.name.match(/-([^-]*)$/)[1]
301
- raise CipherError, 'unknown cipher mode' if mode.nil?
302
- mode.downcase
303
- end
304
-
305
- def authenticated?
306
- @conf.authenticated? or !!@intg
307
- end
308
-
309
- def authenticate!
310
- @conf.final
311
- if @intg
312
- @intg.update @esn.to_s if @esn
313
- @intg.digest[0, @icv_length] == @icv
314
- else
315
- true
316
- end
317
- rescue OpenSSL::Cipher::CipherError
318
- false
319
- end
320
-
321
- def encipher(data)
322
- enciphered_data = @conf.update(data)
323
- @intg.update(enciphered_data) if @intg
324
- enciphered_data
325
- end
326
-
327
- def decipher(data)
328
- @intg.update(data) if @intg
329
- @conf.update(data)
330
- end
331
-
332
293
  def get_auth_data(opt)
333
294
  ad = self[:spi].to_s
334
295
  if opt[:esn]
@@ -431,7 +392,10 @@ module PacketGen
431
392
 
432
393
  IP.bind_header ESP, protocol: ESP::IP_PROTOCOL
433
394
  IPv6.bind_header ESP, next: ESP::IP_PROTOCOL
434
- UDP.bind_header ESP, dport: ESP::UDP_PORT, sport: ESP::UDP_PORT
395
+ UDP.bind_header ESP, procs: [ ->(f) { f.dport = f.sport = ESP::UDP_PORT },
396
+ ->(f) { (f.dport == ESP::UDP_PORT ||
397
+ f.sport == ESP::UDP_PORT) &&
398
+ Types::Int32.new.read(f.body[0..3]).to_i > 0 }]
435
399
  ESP.bind_header IP, next: 4
436
400
  ESP.bind_header IPv6, next: 41
437
401
  ESP.bind_header TCP, next: TCP::IP_PROTOCOL
@@ -0,0 +1,235 @@
1
+ module PacketGen
2
+ module Header
3
+
4
+ # This class handles a pseudo-header used to differentiate ESP from IKE headers
5
+ # in a UDP datagram with port 4500.
6
+ # @author Sylvain Daubert
7
+ # @since 2.0.0
8
+ class NonESPMarker < Base
9
+ # @!attribute non_esp_marker
10
+ # 32-bit zero marker to differentiate IKE packet over UDP port 4500 from ESP ones
11
+ # @return [Integer]
12
+ define_field :non_esp_marker, Types::Int32, default: 0
13
+ # @!attribute body
14
+ # @return [Types::String,Header::Base]
15
+ define_field :body, Types::String
16
+
17
+ # Check non_esp_marker field
18
+ # @see [Base#parse?]
19
+ def parse?
20
+ non_esp_marker == 0
21
+ end
22
+ end
23
+
24
+ # IKE is the Internet Key Exchange protocol (RFC 7296). Ony IKEv2 is supported.
25
+ #
26
+ # A IKE header consists of a header, and a set of payloads. This class
27
+ # handles IKE header. For payloads, see {IKE::Payload}.
28
+ #
29
+ # == IKE header
30
+ # The format of a IKE header is shown below:
31
+ # 1 2 3
32
+ # 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
33
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
34
+ # | IKE SA Initiator's SPI |
35
+ # | |
36
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37
+ # | IKE SA Responder's SPI |
38
+ # | |
39
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
40
+ # | Next Payload | MjVer | MnVer | Exchange Type | Flags |
41
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
42
+ # | Message ID |
43
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
44
+ # | Length |
45
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46
+ # A IKE header consists of:
47
+ # * a IKE SA initiator SPI ({#init_spi}, {Types::Int64} type),
48
+ # * a IKE SA responder SPI ({#resp_spi}, {Types::Int64} type),
49
+ # * a Next Payload field ({#next}, {Types::Int8} type),
50
+ # * a Version field ({#version}, {Types::Int8} type, with first 4-bit field
51
+ # as major number, and last 4-bit field as minor number),
52
+ # * a Exchange type ({#exchange_type}, {Types::Int8} type),
53
+ # * a {#flags} field ({Types::Int8} type),
54
+ # * a Message ID ({#message_id}, {Types::Int32} type),
55
+ # * and a {#length} ({Types::Int32} type).
56
+ #
57
+ # == Create a IKE header
58
+ # === Standalone
59
+ # ike = PacketGen::Header::IKE.new
60
+ # === Classical IKE packet
61
+ # pkt = PacketGen.gen('IP').add('UDP').add('IKE')
62
+ # # access to IKE header
63
+ # pkt.ike # => PacketGen::Header::IKE
64
+ # === NAT-T IKE packet
65
+ # # NonESPMarker is used to insert a 32-bit null field between UDP header
66
+ # # and IKE one to differentiate it from ESP-in-UDP (see RFC 3948)
67
+ # pkt = PacketGen.gen('IP').add('UDP').add('NonESPMarker').add('IKE)
68
+ # @author Sylvain Daubert
69
+ # @since 2.0.0
70
+ class IKE < Base
71
+
72
+ # Classical well-known UDP port for IKE
73
+ UDP_PORT1 = 500
74
+ # Well-known UDP port for IKE when NAT is detected
75
+ UDP_PORT2 = 4500
76
+
77
+ PROTO_IKE = 1
78
+ PROTO_AH = 2
79
+ PROTO_ESP = 3
80
+
81
+ TYPE_IKE_SA_INIT = 34
82
+ TYPE_IKE_AUTH = 35
83
+ TYPE_CREATE_CHILD_SA = 36
84
+ TYPE_INFORMATIONAL = 37
85
+
86
+ # @!attribute init_spi
87
+ # 64-bit initiator SPI
88
+ # @return [Integer]
89
+ define_field :init_spi, Types::Int64
90
+ # @!attribute resp_spi
91
+ # 64-bit responder SPI
92
+ # @return [Integer]
93
+ define_field :resp_spi, Types::Int64
94
+ # @!attribute next
95
+ # 8-bit next payload type
96
+ # @return [Integer]
97
+ define_field :next, Types::Int8
98
+ # @!attribute version
99
+ # 8-bit IKE version
100
+ # @return [Integer]
101
+ define_field :version, Types::Int8, default: 0x20
102
+ # @!attribute [r] exchange_type
103
+ # 8-bit exchange type
104
+ # @return [Integer]
105
+ define_field :exchange_type, Types::Int8
106
+ # @!attribute flags
107
+ # 8-bit flags
108
+ # @return [Integer]
109
+ define_field :flags, Types::Int8
110
+ # @!attribute message_id
111
+ # 32-bit message ID
112
+ # @return [Integer]
113
+ define_field :message_id, Types::Int32
114
+ # @!attribute length
115
+ # 32-bit length of total message (header + payloads)
116
+ # @return [Integer]
117
+ define_field :length, Types::Int32
118
+
119
+ # Defining a body permits using Packet#parse to parse IKE payloads.
120
+ # But this method is hidden as prefered way to access payloads is via #payloads
121
+ define_field :body, Types::String
122
+
123
+ # @!attribute mjver
124
+ # 4-bit major version value
125
+ # @return [Integer]
126
+ # @!attribute mnver
127
+ # 4-bit minor version value
128
+ # @return [Integer]
129
+ define_bit_fields_on :version, :mjver, 4, :mnver, 4
130
+
131
+ # @!attribute rsv1
132
+ # @return [Integer]
133
+ # @!attribute rsv2
134
+ # @return [Integer]
135
+ # @!attribute flag_i
136
+ # bit set in message sent by the original initiator
137
+ # @return [Boolean]
138
+ # @!attribute flag_r
139
+ # indicate this message is a response to a message containing the same Message ID
140
+ # @return [Boolean]
141
+ # @!attribute flag_v
142
+ # version flag. Ignored by IKEv2 peers, and should be set to 0
143
+ # @return [Boolean]
144
+ define_bit_fields_on :flags, :rsv1, 2, :flag_r, :flag_v, :flag_i, :rsv2, 3
145
+
146
+ # @param [Hash] options
147
+ # @see Base#initialize
148
+ def initialize(options={})
149
+ super
150
+ calc_length unless options[:length]
151
+ self.type = options[:type] if options[:type]
152
+ self.type = options[:exchange_type] if options[:exchange_type]
153
+ end
154
+
155
+ # Set exchange type
156
+ # @param [Integer,String] value
157
+ # @return [Integer]
158
+ def exchange_type=(value)
159
+ type = case value
160
+ when Integer
161
+ value
162
+ else
163
+ c = self.class.constants.grep(/TYPE_#{value}/).first
164
+ c ? self.class.const_get(c) : nil
165
+ end
166
+ raise ArgumentError, "unknown exchange type #{value.inspect}" unless type
167
+ self[:exchange_type].value = type
168
+ end
169
+ alias type exchange_type
170
+ alias type= exchange_type=
171
+
172
+ # Get exchange type name
173
+ # @return [String
174
+ def human_exchange_type
175
+ name = self.class.constants.grep(/TYPE_/).
176
+ select { |c| self.class.const_get(c) == type }.
177
+ first || "type #{type}"
178
+ name.to_s.sub(/TYPE_/, '')
179
+ end
180
+ alias human_type human_exchange_type
181
+
182
+ # Calculate length field
183
+ # @return [Integer]
184
+ def calc_length
185
+ self[:length].value = self.sz
186
+ end
187
+
188
+ # IKE payloads
189
+ # @return [Array<Payload>]
190
+ def payloads
191
+ payloads = []
192
+ body = self.body
193
+ while body.is_a?(Payload) do
194
+ payloads << body
195
+ body = body.body
196
+ end
197
+ payloads
198
+ end
199
+
200
+ # @return [String]
201
+ def inspect
202
+ str = Inspect.dashed_line(self.class, 2)
203
+ to_h.each do |attr, value|
204
+ next if attr == :body
205
+ case attr
206
+ when :flags
207
+ str_flags = ''
208
+ %w(r v i).each do |flag|
209
+ str_flags << (send("flag_#{flag}?") ? flag.upcase : '.')
210
+ end
211
+ str << Inspect.shift_level(2)
212
+ str << Inspect::FMT_ATTR % [value.class.to_s.sub(/.*::/, ''), attr,
213
+ str_flags]
214
+ when :exchange_type
215
+ str << Inspect.shift_level(2)
216
+ str << Inspect::FMT_ATTR % [value.class.to_s.sub(/.*::/, ''), attr,
217
+ human_exchange_type]
218
+ else
219
+ str << Inspect.inspect_attribute(attr, value, 2)
220
+ end
221
+ end
222
+ str
223
+ end
224
+ end
225
+
226
+ self.add_class IKE
227
+ self.add_class NonESPMarker
228
+
229
+ UDP.bind_header IKE, dport: IKE::UDP_PORT1, sport: IKE::UDP_PORT1
230
+ UDP.bind_header NonESPMarker, dport: IKE::UDP_PORT2, sport: IKE::UDP_PORT2
231
+ NonESPMarker.bind_header IKE
232
+ end
233
+ end
234
+
235
+ require_relative 'ike/payload'
@@ -0,0 +1,197 @@
1
+ # coding: utf-8
2
+ module PacketGen
3
+ module Header
4
+ class IKE
5
+
6
+ # This class handles Authentication payloads.
7
+ #
8
+ # A AUTH payload consists of the IKE generic payload header (see {Payload})
9
+ # and some specific fields:
10
+ # 1 2 3
11
+ # 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
12
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
13
+ # | Next Payload |C| RESERVED | Payload Length |
14
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
15
+ # | Auth Method | RESERVED |
16
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
17
+ # | |
18
+ # ~ Authentication Data ~
19
+ # | |
20
+ # +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
21
+ # These specific fields are:
22
+ # * {#type} (ID type),
23
+ # * {#reserved},
24
+ # * and {#content} (Identification Data).
25
+ #
26
+ # == Create a KE payload
27
+ # # create a IKE packet with a Auth payload
28
+ # pkt = PacketGen.gen('IP').add('UDP').add('IKE').add('IKE::Auth', method: 'SHARED_KEY')
29
+ # pkt.calc_length
30
+ # @author Sylvain Daubert
31
+ class Auth < Payload
32
+
33
+ # Payload type number
34
+ PAYLOAD_TYPE = 39
35
+
36
+ METHOD_RSA_SIGNATURE = 1
37
+ METHOD_SHARED_KEY = 2
38
+ METHOD_DSA_SIGNATURE = 3
39
+ METHOD_ECDSA256 = 9
40
+ METHOD_ECDSA384 = 10
41
+ METHOD_ECDSA512 = 11
42
+ METHOD_PASSWORD = 12
43
+ METHOD_NULL = 13
44
+ METHOD_DIGITAL_SIGNATURE = 14
45
+
46
+ # @attribute :u32
47
+ # 32-bit word including ID Type and RESERVED fields
48
+ # @return [Integer]
49
+ define_field_before :content, :u32, Types::Int32
50
+ # @attribute [r] method
51
+ # 8-bit Auth Method
52
+ # @return [Integer]
53
+ # @attribute reserved
54
+ # 24-bit reserved field
55
+ # @return [Integer]
56
+ define_bit_fields_on :u32, :method, 8, :reserved, 24
57
+
58
+ # Check authentication (see RFC 7296 §2.15)
59
+ # @param [Packet] init_msg first IKE message sent by peer
60
+ # @param [String] nonce my nonce, sent in first message
61
+ # @param [String] sk_p secret key used to compute prf(SK_px, IDx')
62
+ # @param [Integer] prf PRF type to use (see {Transform}+::PRF_*+ constants)
63
+ # @param [String] shared_secret shared secret to use as PSK (shared secret
64
+ # method only)
65
+ # @param [OpenSSL::X509::Certificate] cert certificate to check AUTH signature,
66
+ # if not embedded in IKE message
67
+ # @return [Boolean]
68
+ # @note For now, only NULL, SHARED_KEY and RSA, DSA and ECDSA signatures are
69
+ # supported.
70
+ # @note For certificates, only check AUTH authenticity with given (or guessed
71
+ # from packet) certificate, but certificate chain is not verified.
72
+ def check?(init_msg: nil, nonce: '', sk_p: '', prf: 1, shared_secret: '',
73
+ cert: nil)
74
+ raise TypeError, 'init_msg should be a Packet' unless init_msg.is_a?(Packet)
75
+ signed_octets = init_msg.ike.to_s
76
+ signed_octets << nonce
77
+ id = packet.ike.flag_i? ? packet.ike_idi : packet.ike_idr
78
+ signed_octets << prf(prf, sk_p, id.to_s[4, id.length - 4])
79
+
80
+ case method
81
+ when METHOD_SHARED_KEY
82
+ auth = prf(prf(shared_secret, 'Key Pad for IKEv2'), signed_octets)
83
+ auth == content
84
+ when METHOD_RSA_SIGNATURE, METHOD_ECDSA256, METHOD_ECDSA384, METHOD_ECDSA512
85
+ if packet.ike_cert
86
+ # FIXME: Expect a ENCODING_X509_CERT_SIG
87
+ # Others types not supported for now...
88
+ cert = OpenSSL::X509::Certificate.new(packet.ike_cert.content)
89
+ elsif cert.nil?
90
+ raise CryptoError, 'a certificate should be provided'
91
+ end
92
+
93
+ text = cert.to_text
94
+ m = text.match(/Public Key Algorithm: ([a-zA-Z0-9-]+)/)
95
+ digest = case m[1]
96
+ when 'id-ecPublicKey'
97
+ m2 = text.match(/Public-Key: \((\d+) bit\)/)
98
+ case m2[1]
99
+ when '256'
100
+ OpenSSL::Digest::SHA256.new
101
+ when '384'
102
+ OpenSSL::Digest::SHA384.new
103
+ when '521'
104
+ OpenSSL::Digest::SHA512.new
105
+ end
106
+ when /sha([235]\d+)/
107
+ OpenSSL::Digest.const_get("SHA#{$1}").new
108
+ when /sha1/, 'rsaEncryption'
109
+ OpenSSL::Digest::SHA1.new
110
+ end
111
+ signature = format_signature(cert.public_key, content.to_s)
112
+ cert.public_key.verify(digest, signature, signed_octets)
113
+ when METHOD_NULL
114
+ true
115
+ else
116
+ raise NotImplementedError, "unsupported method #{human_method}"
117
+ end
118
+ end
119
+
120
+ # Set Auth method
121
+ # @param [Integer,String] value
122
+ # @return [Integer]
123
+ def method=(value)
124
+ method = case value
125
+ when Integer
126
+ value
127
+ else
128
+ c = self.class.constants.grep(/METHOD_#{value}/).first
129
+ c ? self.class.const_get(c) : nil
130
+ end
131
+ raise ArgumentError, "unknown auth method #{value.inspect}" unless method
132
+ self[:u32].value = (self[:u32].to_i & 0xffffff) | (method << 24)
133
+ end
134
+
135
+ # Get authentication method name
136
+ # @return [String]
137
+ def human_method
138
+ name = self.class.constants.grep(/METHOD_/).
139
+ select { |c| self.class.const_get(c) == method }.
140
+ first || "method #{method}"
141
+ name.to_s.sub(/METHOD_/, '')
142
+ end
143
+
144
+ # @return [String]
145
+ def inspect
146
+ str = Inspect.dashed_line(self.class, 2)
147
+ fields.each do |attr|
148
+ case attr
149
+ when :body
150
+ next
151
+ when :u32
152
+ str << Inspect.shift_level(2)
153
+ str << Inspect::FMT_ATTR % ['Int8', :method, human_method]
154
+ str << Inspect.inspect_attribute(:reserved, self.reserved, 2)
155
+ else
156
+ str << Inspect.inspect_attribute(attr, self[attr], 2)
157
+ end
158
+ end
159
+ str
160
+ end
161
+
162
+ private
163
+
164
+ def prf(type, key, msg)
165
+ case type
166
+ when Transform::PRF_HMAC_MD5, Transform::PRF_HMAC_SHA1,
167
+ Transform::PRF_HMAC_SHA2_256, Transform::PRF_HMAC_SHA2_384,
168
+ Transform::PRF_HMAC_SHA2_512
169
+ digestname = Transform.constants.grep(/PRF_/).
170
+ select { |c| Transform.const_get(c) == type }.first.
171
+ to_s.sub(/^PRF_HMAC_/, '').sub(/2_/, '')
172
+ digest = OpenSSL::Digest.const_get(digestname).new
173
+ else
174
+ raise NotImplementedError, 'for now, only HMAC-based PRF are supported'
175
+ end
176
+ hmac = OpenSSL::HMAC.new(key, digest)
177
+ hmac << msg
178
+ hmac.digest
179
+ end
180
+
181
+ def format_signature(pkey, sig)
182
+ if pkey.is_a?(OpenSSL::PKey::EC)
183
+ # PKey::EC need a signature as a DER string representing a sequence of
184
+ # 2 integers: r and s
185
+ r = OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(sig[0, sig.size / 2], 2).to_i)
186
+ s = OpenSSL::ASN1::Integer.new(OpenSSL::BN.new(sig[sig.size / 2,
187
+ sig.size / 2], 2).to_i)
188
+ OpenSSL::ASN1::Sequence.new([r, s]).to_der
189
+ else
190
+ sig
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
197
+