rasn1 0.12.0 → 0.12.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 552859c3f49caec5374c8db4459ad23e1f9c43e930b0d6511cab8f997c798d20
4
- data.tar.gz: 29eaf2fc529b470c9bc6283efbaf82e580d5b4b86d5c46920ee4a882a18b0d72
3
+ metadata.gz: 884fa6f74d0b3074ef074d061301092d55df580b663714c4a962a7b8503d777d
4
+ data.tar.gz: 77cc3c5d7bf926463464ca4d37237f0cddc358e0d7b9aa3e85c15982ab3f3fc1
5
5
  SHA512:
6
- metadata.gz: 9e5df0e417db7e1f02accae9cc4f17b4a49975d1dc17b716dbf587bb3f5e8ca68ed842d22358d7de037041255c164df5a92c6ada52adba944f0ac635793a64b1
7
- data.tar.gz: e193284ea13ca5f80a460b8b390839a0283a8424ca8201f24cbd6113919399fcd5b83a2fc39dd5b26925187e19930a1aceccb1485255de8301435e20a2c82761
6
+ metadata.gz: fee544874a10b10618cd596b1c30edd4ad70185a783eeadbe23a86fa2439339d7d025784be5adc76904e05856ac8791817a768c0603ece0141e38d2975e588ad
7
+ data.tar.gz: 39428eb166401712d44eb60c4413bcd919d4fca380213acbe1dc29c32425c03a8852e1f0afa8934f5a04c604b376ac3c47424754b358cae96cc685029c467da7
data/lib/rasn1/errors.rb CHANGED
@@ -18,7 +18,7 @@ module RASN1
18
18
  # Enumerated error
19
19
  class EnumeratedError < Error; end
20
20
 
21
- # CHOICE error: #chosen not set
21
+ # CHOICE error: {Types::Choice#chosen} not set
22
22
  class ChoiceError < RASN1::Error
23
23
  # @param [Types::Base] object
24
24
  def initialize(object)
@@ -26,6 +26,7 @@ module RASN1
26
26
  super()
27
27
  end
28
28
 
29
+ # @return [String]
29
30
  def message
30
31
  "CHOICE #{@object.name}: #chosen not set"
31
32
  end
data/lib/rasn1/model.rb CHANGED
@@ -66,6 +66,9 @@ module RASN1
66
66
  class Model # rubocop:disable Metrics/ClassLength
67
67
  # @private
68
68
  Elem = Struct.new(:name, :proc_or_class, :content) do
69
+ # @param [String,Symbol] name
70
+ # @param [Proc,Class] proc_or_class
71
+ # @param [Hash,nil] content
69
72
  def initialize(name, proc_or_class, content)
70
73
  if content.is_a?(Array)
71
74
  duplicate_names = find_all_duplicate_names(content.map(&:name) + [name])
@@ -88,11 +91,13 @@ module RASN1
88
91
 
89
92
  # @private
90
93
  WrapElem = Struct.new(:element, :options) do
94
+ # @return [String]
91
95
  def name
92
96
  "#{element.name}_wrapper"
93
97
  end
94
98
  end
95
99
 
100
+ # Define helper methods to define models
96
101
  module Accel
97
102
  # @return [Hash]
98
103
  attr_reader :options
@@ -145,6 +150,9 @@ module RASN1
145
150
  klass.class_eval { @root = root }
146
151
  end
147
152
 
153
+ # @private
154
+ # @param [String,Symbol] accel_name
155
+ # @param [Class] klass
148
156
  # @since 0.11.0
149
157
  # @since 0.12.0 track source location on error (adfoster-r7)
150
158
  def define_type_accel_base(accel_name, klass)
@@ -159,6 +167,9 @@ module RASN1
159
167
  EVAL
160
168
  end
161
169
 
170
+ # @private
171
+ # @param [String,Symbol] accel_name
172
+ # @param [Class] klass
162
173
  # @since 0.11.0
163
174
  # @since 0.12.0 track source location on error (adfoster-r7)
164
175
  def define_type_accel_of(accel_name, klass)
@@ -175,8 +186,9 @@ module RASN1
175
186
 
176
187
  # Define an accelarator to access a type in a model definition
177
188
  # @param [String] accel_name
178
- # @param [Class] klass
189
+ # @param [Class] klass class to instanciate
179
190
  # @since 0.11.0
191
+ # @since 0.12.0 track source location on error (adfoster-r7)
180
192
  def define_type_accel(accel_name, klass)
181
193
  if klass < Types::SequenceOf
182
194
  define_type_accel_of(accel_name, klass)
@@ -0,0 +1,174 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ # @private
5
+ class Tracer
6
+ # @return [IO]
7
+ attr_reader :io
8
+ # @return [Integer]
9
+ attr_accessor :tracing_level
10
+
11
+ TRACED_CLASSES = [Types::Any, Types::Choice, Types::Sequence, Types::SequenceOf, Types::Base].freeze
12
+
13
+ # @param [IO] io
14
+ def initialize(io)
15
+ @io = io
16
+ @tracing_level = 0
17
+ end
18
+
19
+ # Puts +msg+ onto {#io}.
20
+ # @param [String] msg
21
+ # @return [void]
22
+ def trace(msg)
23
+ @io.puts(indent << msg)
24
+ end
25
+
26
+ # Return identation for given +level+. If +nil+, use {#tracing_level}.
27
+ # @param [Integer,nil] level
28
+ # @return [String]
29
+ def indent(level=nil)
30
+ level ||= @tracing_level
31
+ ' ' * level
32
+ end
33
+ end
34
+
35
+ # Trace RASN1 parsing to +io+.
36
+ # All parsing methods called in block are traced to +io+. Each ASN.1 element is
37
+ # traced in a line showing element's id, its length and its data.
38
+ # @param [IO] io
39
+ # @example
40
+ # RASN1.trace do
41
+ # RASN1.parse("\x02\x01\x01") # puts "INTEGER id: 2 (0x02), len: 1 (0x01), data: 0x01"
42
+ # end
43
+ # RASN1.parse("\x01\x01\xff") # puts nothing onto STDOUT
44
+ # @return [void]
45
+ def self.trace(io=$stdout)
46
+ @tracer = Tracer.new(io)
47
+ Tracer::TRACED_CLASSES.each(&:start_tracing)
48
+
49
+ begin
50
+ yield @tracer
51
+ ensure
52
+ Tracer::TRACED_CLASSES.reverse.each(&:stop_tracing)
53
+ @tracer.io.flush
54
+ @tracer = nil
55
+ end
56
+ end
57
+
58
+ # @private
59
+ def self.tracer
60
+ @tracer
61
+ end
62
+
63
+ module Types
64
+ class Base
65
+ class << self
66
+ # @private
67
+ # Patch {#do_parse} to add tracing ability
68
+ def start_tracing
69
+ alias_method :do_parse_without_tracing, :do_parse
70
+ alias_method :do_parse, :do_parse_with_tracing
71
+ alias_method :do_parse_explicit_without_tracing, :do_parse_explicit
72
+ alias_method :do_parse_explicit, :do_parse_explicit_with_tracing
73
+ end
74
+
75
+ # @private
76
+ # Unpatch {#do_parse} to remove tracing ability
77
+ def stop_tracing
78
+ alias_method :do_parse, :do_parse_without_tracing # rubocop:disable Lint/DuplicateMethods
79
+ alias_method :do_parse_explicit, :do_parse_explicit_without_tracing # rubocop:disable Lint/DuplicateMethods
80
+ end
81
+ end
82
+
83
+ # @private
84
+ # Parse +der+ with tracing abillity
85
+ # @see #parse!
86
+ def do_parse_with_tracing(der, ber)
87
+ ret = do_parse_without_tracing(der, ber)
88
+ RASN1.tracer.trace(self.trace)
89
+ ret
90
+ end
91
+
92
+ def do_parse_explicit_with_tracing(data)
93
+ RASN1.tracer.tracing_level += 1
94
+ do_parse_explicit_without_tracing(data)
95
+ RASN1.tracer.tracing_level -= 1
96
+ end
97
+ end
98
+
99
+ class Choice
100
+ class << self
101
+ # @private
102
+ # Patch {#parse!} to add tracing ability
103
+ def start_tracing
104
+ alias_method :parse_without_tracing, :parse!
105
+ alias_method :parse!, :parse_with_tracing
106
+ end
107
+
108
+ # @private
109
+ # Unpatch {#parse!} to remove tracing ability
110
+ def stop_tracing
111
+ alias_method :parse!, :parse_without_tracing # rubocop:disable Lint/DuplicateMethods
112
+ end
113
+ end
114
+
115
+ # @private
116
+ # Parse +der+ with tracing abillity
117
+ # @see #parse!
118
+ def parse_with_tracing(der, ber: false)
119
+ RASN1.tracer.trace(self.trace)
120
+ parse_without_tracing(der, ber: ber)
121
+ end
122
+ end
123
+
124
+ class Sequence
125
+ class << self
126
+ # @private
127
+ # Patch {#der_to_value} to add tracing ability
128
+ def start_tracing
129
+ alias_method :der_to_value_without_tracing, :der_to_value
130
+ alias_method :der_to_value, :der_to_value_with_tracing
131
+ end
132
+
133
+ # @private
134
+ # Unpatch {#der_to_value!} to remove tracing ability
135
+ def stop_tracing
136
+ alias_method :der_to_value, :der_to_value_without_tracing # rubocop:disable Lint/DuplicateMethods
137
+ end
138
+ end
139
+
140
+ # @private
141
+ # der_to_value +der+ with tracing abillity
142
+ def der_to_value_with_tracing(der, ber: false)
143
+ RASN1.tracer.tracing_level += 1
144
+ der_to_value_without_tracing(der, ber: ber)
145
+ RASN1.tracer.tracing_level -= 1
146
+ end
147
+ end
148
+
149
+ class SequenceOf
150
+ class << self
151
+ # @private
152
+ # Patch {#der_to_value} to add tracing ability
153
+ def start_tracing
154
+ alias_method :der_to_value_without_tracing, :der_to_value
155
+ alias_method :der_to_value, :der_to_value_with_tracing
156
+ end
157
+
158
+ # @private
159
+ # Unpatch {#der_to_value!} to remove tracing ability
160
+ def stop_tracing
161
+ alias_method :der_to_value, :der_to_value_without_tracing # rubocop:disable Lint/DuplicateMethods
162
+ end
163
+ end
164
+
165
+ # @private
166
+ # der_to_value +der+ with tracing abillity
167
+ def der_to_value_with_tracing(der, ber: false)
168
+ RASN1.tracer.tracing_level += 1
169
+ der_to_value_without_tracing(der, ber: ber)
170
+ RASN1.tracer.tracing_level -= 1
171
+ end
172
+ end
173
+ end
174
+ end
@@ -31,22 +31,12 @@ module RASN1
31
31
  # @param [Boolean] ber if +true+, accept BER encoding
32
32
  # @return [Integer] total number of parsed bytes
33
33
  def parse!(der, ber: false)
34
- if der.empty?
35
- return 0 if optional?
36
-
37
- raise ASN1Error, 'Expected ANY but get nothing'
38
- end
39
-
40
- id_size = Types.decode_identifier_octets(der).last
41
- total_length, = get_data(der[id_size..-1], ber)
42
- total_length += id_size
43
-
44
- @no_value = false
45
- @value = der[0, total_length]
46
-
34
+ total_length, _data = do_parse(der, ber)
47
35
  total_length
48
36
  end
49
37
 
38
+ # @param [::Integer] level
39
+ # @return [String]
50
40
  def inspect(level=0)
51
41
  str = common_inspect(level)
52
42
  str << if !value?
@@ -60,6 +50,16 @@ module RASN1
60
50
  end
61
51
  end
62
52
 
53
+ # @private Tracer private API
54
+ # @return [String]
55
+ def trace
56
+ return trace_any if value?
57
+
58
+ msg_type(no_id: true) << ' NONE'
59
+ end
60
+
61
+ private
62
+
63
63
  def common_inspect(level)
64
64
  lvl = level >= 0 ? level : 0
65
65
  str = ' ' * lvl
@@ -69,6 +69,27 @@ module RASN1
69
69
  str << "[#{id}] IMPLICIT " if implicit?
70
70
  str << '(ANY) '
71
71
  end
72
+
73
+ def do_parse(der, ber)
74
+ if der.empty?
75
+ return [0, ''] if optional?
76
+
77
+ raise ASN1Error, 'Expected ANY but get nothing'
78
+ end
79
+
80
+ id_size = Types.decode_identifier_octets(der).last
81
+ total_length, = get_data(der[id_size..-1], ber)
82
+ total_length += id_size
83
+
84
+ @no_value = false
85
+ @value = der[0, total_length]
86
+
87
+ [total_length, @value]
88
+ end
89
+
90
+ def trace_any
91
+ msg_type(no_id: true) << trace_data(value)
92
+ end
72
93
  end
73
94
  end
74
95
  end
@@ -71,6 +71,12 @@ module RASN1
71
71
  attr_reader :default
72
72
  # @return [Hash[Symbol, Object]]
73
73
  attr_reader :options
74
+ # @return [String] raw parsed data
75
+ attr_reader :raw_data
76
+ # @return [String] raw parsed length
77
+ attr_reader :raw_length
78
+
79
+ private :raw_data, :raw_length
74
80
 
75
81
  # Get ASN.1 type
76
82
  # @return [String]
@@ -121,13 +127,16 @@ module RASN1
121
127
  set_value(options.delete(:value))
122
128
  self.options = options
123
129
  specific_initializer
130
+ @raw_data = ''.b
131
+ @raw_length = ''.b
124
132
  end
125
133
 
126
134
  # @abstract To help subclass initialize itself. Default implementation do nothing.
127
135
  def specific_initializer; end
128
136
 
129
- # Used by +#dup+ and +#clone+. Deep copy @value and @default.
130
- def initialize_copy(_other)
137
+ # Deep copy @value and @default.
138
+ def initialize_copy(*)
139
+ super
131
140
  @value = @value.dup
132
141
  @no_value = @no_value.dup
133
142
  @default = @default.dup
@@ -153,6 +162,7 @@ module RASN1
153
162
  ''
154
163
  end
155
164
 
165
+ # Say if this type is optional
156
166
  # @return [::Boolean]
157
167
  def optional?
158
168
  @optional
@@ -215,17 +225,11 @@ module RASN1
215
225
  # @return [Integer] total number of parsed bytes
216
226
  # @raise [ASN1Error] error on parsing
217
227
  def parse!(der, ber: false)
218
- return 0 unless check_id(der)
228
+ total_length, data = do_parse(der, ber)
229
+ return 0 if total_length.zero?
219
230
 
220
- id_size = Types.decode_identifier_octets(der).last
221
- total_length, data = get_data(der[id_size..-1], ber)
222
- total_length += id_size
223
- @no_value = false
224
231
  if explicit?
225
- # Delegate to #explicit type to generate sub-value
226
- type = explicit_type
227
- type.parse!(data)
228
- @value = type.value
232
+ do_parse_explicit(data)
229
233
  else
230
234
  der_to_value(data, ber: ber)
231
235
  end
@@ -283,8 +287,82 @@ module RASN1
283
287
  value? && (@default.nil? || (@value != @default))
284
288
  end
285
289
 
290
+ # @private Tracer private API
291
+ # @return [String]
292
+ def trace
293
+ return trace_real if value?
294
+
295
+ msg = msg_type
296
+ if default.nil? # rubocop:disable Style/ConditionalAssignment
297
+ msg << ' NONE'
298
+ else
299
+ msg << " DEFAULT VALUE #{default}"
300
+ end
301
+ end
302
+
286
303
  private
287
304
 
305
+ def trace_real
306
+ encoded_id = unpack(encode_identifier_octets)
307
+ data_length = raw_data.length
308
+ encoded_length = unpack(raw_length)
309
+ msg = msg_type
310
+ msg << " (0x#{encoded_id}),"
311
+ msg << " len: #{data_length} (0x#{encoded_length})"
312
+ msg << trace_data
313
+ end
314
+
315
+ def trace_data(data=nil)
316
+ byte_count = 0
317
+ data ||= raw_data
318
+
319
+ lines = []
320
+ str = ''
321
+ data.each_byte do |byte|
322
+ if (byte_count % 16).zero?
323
+ str = trace_format_new_data_line(byte_count)
324
+ lines << str
325
+ end
326
+ str[compute_trace_index(byte_count, 3), 2] = '%02x' % byte
327
+ str[compute_trace_index(byte_count, 1, 49)] = byte >= 32 && byte <= 126 ? byte.chr : '.'
328
+ byte_count += 1
329
+ end
330
+ lines.map(&:rstrip).join << "\n"
331
+ end
332
+
333
+ def trace_format_new_data_line(count)
334
+ head_line = RASN1.tracer.indent(RASN1.tracer.tracing_level + 1)
335
+ ("\n#{head_line}%04x " % count) << ' ' * 68
336
+ end
337
+
338
+ def compute_trace_index(byte_count, byte_count_mul=1, offset=0)
339
+ base_idx = 7 + RASN1.tracer.indent(RASN1.tracer.tracing_level + 1).length
340
+ base_idx + offset + (byte_count % 16) * byte_count_mul
341
+ end
342
+
343
+ def unpack(binstr)
344
+ binstr.unpack1('H*')
345
+ end
346
+
347
+ def asn1_class_to_s
348
+ asn1_class == :universal ? '' : asn1_class.to_s.upcase << ' '
349
+ end
350
+
351
+ def msg_type(no_id: false)
352
+ msg = name.nil? ? +'' : +"#{name} "
353
+ msg << "[ #{asn1_class_to_s}#{id} ] " unless no_id
354
+ msg << if explicit?
355
+ +'EXPLICIT '
356
+ elsif implicit?
357
+ +'IMPLICIT '
358
+ else
359
+ +''
360
+ end
361
+ msg << type
362
+ msg << ' OPTIONAL' if optional?
363
+ msg
364
+ end
365
+
288
366
  def pc_bit
289
367
  if @constructed.nil?
290
368
  self.class.const_get(:ASN1_PC)
@@ -299,7 +377,7 @@ module RASN1
299
377
  lvl = level >= 0 ? level : 0
300
378
  str = ' ' * lvl
301
379
  str << "#{@name} " unless @name.nil?
302
- str << asn1_class.to_s.upcase << ' ' unless asn1_class == :universal
380
+ str << asn1_class_to_s
303
381
  str << "[#{id}] EXPLICIT " if explicit?
304
382
  str << "[#{id}] IMPLICIT " if implicit?
305
383
  str << "#{type}:"
@@ -313,6 +391,24 @@ module RASN1
313
391
  end
314
392
  end
315
393
 
394
+ def do_parse(der, ber)
395
+ return [0, ''] unless check_id(der)
396
+
397
+ id_size = Types.decode_identifier_octets(der).last
398
+ total_length, data = get_data(der[id_size..-1], ber)
399
+ total_length += id_size
400
+ @no_value = false
401
+
402
+ [total_length, data]
403
+ end
404
+
405
+ def do_parse_explicit(data)
406
+ # Delegate to #explicit type to generate sub-value
407
+ type = explicit_type
408
+ type.parse!(data)
409
+ @value = type.value
410
+ end
411
+
316
412
  def value_to_der
317
413
  case @value
318
414
  when Base
@@ -454,6 +550,21 @@ module RASN1
454
550
  end
455
551
 
456
552
  def get_data(der, ber)
553
+ return [0, ''] if der.nil? || der.empty?
554
+
555
+ length, length_length = get_length(der, ber)
556
+
557
+ data = der[1 + length_length, length]
558
+ @raw_length = der[0, length_length + 1]
559
+ @raw_data = data
560
+
561
+ total_length = 1 + length
562
+ total_length += length_length if length_length.positive?
563
+
564
+ [total_length, data]
565
+ end
566
+
567
+ def get_length(der, ber)
457
568
  length = der.unpack1('C').to_i
458
569
  length_length = 0
459
570
 
@@ -464,12 +575,8 @@ module RASN1
464
575
  length = der[1, length_length].unpack('C*')
465
576
  .reduce(0) { |len, b| (len << 8) | b }
466
577
  end
467
- data = der[1 + length_length, length]
468
578
 
469
- total_length = 1 + length
470
- total_length += length_length if length_length.positive?
471
-
472
- [total_length, data]
579
+ [length, length_length]
473
580
  end
474
581
 
475
582
  def raise_on_indefinite_length(ber)
@@ -484,7 +591,7 @@ module RASN1
484
591
  end
485
592
 
486
593
  def explicit_type
487
- self.class.new
594
+ self.class.new(name: name)
488
595
  end
489
596
 
490
597
  def raise_id_error(der)
@@ -493,10 +600,6 @@ module RASN1
493
600
  raise ASN1Error, msg
494
601
  end
495
602
 
496
- def class_from_numeric_id(id)
497
- CLASSES.key(id & CLASS_MASK)
498
- end
499
-
500
603
  def self2name
501
604
  name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
502
605
  if implicit? || explicit?
@@ -8,7 +8,6 @@ module RASN1
8
8
  # BitString id value
9
9
  ID = 3
10
10
 
11
- # @param [Integer] bit_length
12
11
  # @return [Integer]
13
12
  attr_writer :bit_length
14
13
 
@@ -42,6 +41,9 @@ module RASN1
42
41
  str << " #{value.inspect} (bit length: #{bit_length})"
43
42
  end
44
43
 
44
+ # Same as {Base#can_build?} but also check bit_length
45
+ # @see Base#can_build?
46
+ # @return [Boolean]
45
47
  def can_build?
46
48
  super || (!@default.nil? && (@bit_length != @default_bit_length))
47
49
  end
@@ -86,7 +88,7 @@ module RASN1
86
88
  end
87
89
 
88
90
  def explicit_type
89
- self.class.new(value: @value, bit_length: @bit_length)
91
+ self.class.new(name: name, value: @value, bit_length: @bit_length)
90
92
  end
91
93
  end
92
94
  end
@@ -39,6 +39,12 @@ module RASN1
39
39
  @value = true
40
40
  end
41
41
  end
42
+
43
+ def trace_data
44
+ return super if explicit?
45
+
46
+ " #{raw_data == "\x00".b ? 'FALSE' : 'TRUE'} (0x#{raw_data.unpack1('H*')})"
47
+ end
42
48
  end
43
49
  end
44
50
  end
@@ -88,6 +88,8 @@ module RASN1
88
88
  raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
89
89
  end
90
90
 
91
+ # @param [::Integer] level
92
+ # @return [String]
91
93
  def inspect(level=0)
92
94
  str = common_inspect(level)
93
95
  str << if defined? @chosen
@@ -97,6 +99,12 @@ module RASN1
97
99
  end
98
100
  end
99
101
 
102
+ # @private Tracer private API
103
+ # @return [String]
104
+ def trace
105
+ msg_type(no_id: true)
106
+ end
107
+
100
108
  private
101
109
 
102
110
  def check_chosen
@@ -2,11 +2,12 @@
2
2
 
3
3
  module RASN1
4
4
  module Types
5
- # Mixin to had constraints on a RASN1 type.
5
+ # Mixin to add constraints on a RASN1 type.
6
6
  # Should not be used directly but through {Types.define_type}.
7
7
  # @version 0.11.0
8
8
  # @author Sylvain Daubert
9
9
  module Constrained
10
+ # Define class/module methods for {Constrained} module
10
11
  module ClassMethods
11
12
  # Setter for constraint
12
13
  # @param [Proc,nil] constraint
@@ -31,8 +32,10 @@ module RASN1
31
32
  end
32
33
 
33
34
  class << self
35
+ # @return [Proc] proc to check constraints
34
36
  attr_reader :constraint
35
37
 
38
+ # Extend +base+ with {ClassMethods}
36
39
  def included(base)
37
40
  base.extend ClassMethods
38
41
  end
@@ -46,6 +49,8 @@ module RASN1
46
49
  super
47
50
  end
48
51
 
52
+ private
53
+
49
54
  def der_to_value(der, ber: false)
50
55
  super
51
56
  self.class.check_constraint(@value)
@@ -134,6 +134,12 @@ module RASN1
134
134
  raise ASN1Error, "#{prefix}: unrecognized format: #{date_hour}"
135
135
  end
136
136
  end
137
+
138
+ def trace_data
139
+ return super if explicit?
140
+
141
+ +' ' << raw_data
142
+ end
137
143
  end
138
144
  end
139
145
  end
@@ -20,7 +20,7 @@ module RASN1
20
20
  initialize_enum(@options[:enum])
21
21
  end
22
22
 
23
- # @param [Integer,String,Symbol,nil] v
23
+ # @param [Integer,String,Symbol,nil] val
24
24
  # @return [void]
25
25
  def value=(val)
26
26
  @no_value = false
@@ -128,18 +128,29 @@ module RASN1
128
128
  v
129
129
  end
130
130
 
131
+ def int_to_enum(int, check_enum: true)
132
+ raise EnumeratedError, "#{@name}: value #{int} not in enumeration" if check_enum && !@enum.value?(int)
133
+
134
+ @enum.key(int) || int
135
+ end
136
+
131
137
  def der_to_value(der, ber: false)
132
138
  @value = der_to_int_value(der, ber: ber)
133
139
  return if @enum.empty?
134
140
 
135
- int_value = @value
136
- raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless @enum.value?(@value)
137
-
138
- @value = @enum.key(@value)
141
+ @value = int_to_enum(@value)
139
142
  end
140
143
 
141
144
  def explicit_type
142
- self.class.new(name: @name, enum: @enum)
145
+ self.class.new(name: name, enum: enum)
146
+ end
147
+
148
+ def trace_data
149
+ return super if explicit?
150
+ return " #{der_to_int_value(raw_data)} (0x#{raw_data.unpack1('H*')})" if @enum.empty?
151
+
152
+ v = int_to_enum(der_to_int_value(raw_data), check_enum: false)
153
+ " #{v} (0x#{raw_data.unpack1('H*')})"
143
154
  end
144
155
  end
145
156
  end
@@ -15,6 +15,8 @@ module RASN1
15
15
  str
16
16
  end
17
17
 
18
+ # @return [Boolean]
19
+ # Build only if not optional
18
20
  def can_build?
19
21
  !optional?
20
22
  end
@@ -15,6 +15,8 @@ module RASN1
15
15
  # OctetString id value
16
16
  ID = 4
17
17
 
18
+ # @param [::Integer] level
19
+ # @return [String]
18
20
  def inspect(level=0)
19
21
  str = common_inspect(level)
20
22
  str << " #{value.inspect}"
@@ -36,7 +36,8 @@ module RASN1
36
36
  @value ||= []
37
37
  end
38
38
 
39
- def initialize_copy(other)
39
+ # Deep copy @value
40
+ def initialize_copy(*)
40
41
  super
41
42
  @value = case @value
42
43
  when Array
@@ -88,8 +89,12 @@ module RASN1
88
89
  end
89
90
  end
90
91
 
92
+ def trace_data
93
+ ''
94
+ end
95
+
91
96
  def explicit_type
92
- self.class.new(value: @value)
97
+ self.class.new(name: name, value: @value)
93
98
  end
94
99
  end
95
100
  end
@@ -67,7 +67,8 @@ module RASN1
67
67
  @no_value = false
68
68
  end
69
69
 
70
- def initialize_copy(other)
70
+ # Clone @#of_type and values
71
+ def initialize_copy(*)
71
72
  super
72
73
  @of_type = @of_type.dup
73
74
  @value = @value.map(&:dup)
@@ -104,6 +105,7 @@ module RASN1
104
105
  @value.length
105
106
  end
106
107
 
108
+ # @param [::Integer] level
107
109
  # @return [String]
108
110
  def inspect(level=0)
109
111
  str = common_inspect(level)
@@ -153,7 +155,7 @@ module RASN1
153
155
  end
154
156
 
155
157
  def explicit_type
156
- self.class.new(self.of_type)
158
+ self.class.new(self.of_type, name: name)
157
159
  end
158
160
 
159
161
  def push_primitive(obj)
@@ -188,6 +190,10 @@ module RASN1
188
190
  raise ASN1Error, "object to add should be a #{of_type_class} or a Hash"
189
191
  end
190
192
  end
193
+
194
+ def trace_data
195
+ ''
196
+ end
191
197
  end
192
198
  end
193
199
  end
@@ -46,6 +46,12 @@ module RASN1
46
46
  century = (Time.now.year / 100).to_s
47
47
  @value = Strptime.new(format).exec(century + der)
48
48
  end
49
+
50
+ def trace_data
51
+ return super if explicit?
52
+
53
+ +' ' << raw_data
54
+ end
49
55
  end
50
56
  end
51
57
  end
data/lib/rasn1/types.rb CHANGED
@@ -87,8 +87,16 @@ module RASN1
87
87
  # @param [Types::Base] from class from which inherits
88
88
  # @param [Module] in_module module in which creates new type (default to {RASN1::Types})
89
89
  # @return [Class] newly created class
90
+ # @yieldparam [Object] value value to set to type, or infered at parsing
91
+ # @yieldreturn [Boolean]
90
92
  # @since 0.11.0
91
93
  # @since 0.12.0 in_module parameter
94
+ # @example
95
+ # # Define a new UInt32 type
96
+ # # UInt32 ::= INTEGER (0 .. 4294967295)
97
+ # RASN1::Types.define_type('UInt32', from: RASN1::Types::Integer) do |value|
98
+ # (value >= 0) && (value < 2**32)
99
+ # end
92
100
  def self.define_type(name, from:, in_module: self, &block)
93
101
  constraint = block.nil? ? nil : block.to_proc
94
102
 
data/lib/rasn1/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RASN1
4
- VERSION = '0.12.0'
4
+ VERSION = '0.12.1'
5
5
  end
data/lib/rasn1/wrapper.rb CHANGED
@@ -14,8 +14,8 @@ module RASN1
14
14
  # @example
15
15
  # # object to wrap
16
16
  # int = RASN1::Types::Integer.new(implicit: 1) # its tag is 0x81
17
- # # simple wraper, change an option
18
- # wrapper = RASN1::Wrapper.new(int, optional: true, default: 1)
17
+ # # simple wrapper, change an option
18
+ # wrapper = RASN1::Wrapper.new(int, default: 1)
19
19
  # # implicit wrapper
20
20
  # wrapper = RASN1::Wrapper.new(int, implicit: 3) # wrapped int tag is now 0x83
21
21
  # # explicit wrapper
@@ -70,26 +70,6 @@ module RASN1
70
70
  super(element)
71
71
  end
72
72
 
73
- def explicit_implicit(options)
74
- opts = options.dup
75
- @explicit = opts.delete(:explicit)
76
- @implicit = opts.delete(:implicit)
77
- opts
78
- end
79
-
80
- def generate_explicit_wrapper(options)
81
- # ExplicitWrapper is a hand-made explicit tag, but we have to use its implicit option
82
- # to force its tag value.
83
- @explicit_wrapper = ExplicitWrapper.new(options.merge(implicit: @explicit))
84
- end
85
-
86
- def generate_explicit_wrapper_options(options)
87
- new_opts = {}
88
- new_opts[:default] = options[:default] if options.key?(:default)
89
- new_opts[:optional] = options[:optional] if options.key?(:optional)
90
- new_opts
91
- end
92
-
93
73
  # Say if wrapper is an explicit one (i.e. add tag and length to its element)
94
74
  # @return [Boolean]
95
75
  def explicit?
@@ -136,6 +116,8 @@ module RASN1
136
116
  end
137
117
  end
138
118
 
119
+ # @return [Boolean]
120
+ # @see Types::Base#value?
139
121
  def value?
140
122
  if explicit?
141
123
  @explicit_wrapper.value?
@@ -151,6 +133,7 @@ module RASN1
151
133
  end
152
134
 
153
135
  # @return [::Integer]
136
+ # @see Types::Base#id
154
137
  def id
155
138
  if implicit?
156
139
  @implicit
@@ -162,6 +145,7 @@ module RASN1
162
145
  end
163
146
 
164
147
  # @return [Symbol]
148
+ # @see Types::Base#asn1_class
165
149
  def asn1_class
166
150
  return element.asn1_class unless @options.key?(:class)
167
151
 
@@ -169,6 +153,7 @@ module RASN1
169
153
  end
170
154
 
171
155
  # @return [Boolean]
156
+ # @see Types::Base#constructed
172
157
  def constructed?
173
158
  return element.constructed? unless @options.key?(:constructed)
174
159
 
@@ -176,10 +161,13 @@ module RASN1
176
161
  end
177
162
 
178
163
  # @return [Boolean]
164
+ # @see Types::Base#primitive
179
165
  def primitive?
180
166
  !constructed?
181
167
  end
182
168
 
169
+ # @param [::Integer] level
170
+ # @return [String]
183
171
  def inspect(level=0)
184
172
  return super(level) unless explicit?
185
173
 
@@ -188,6 +176,26 @@ module RASN1
188
176
 
189
177
  private
190
178
 
179
+ def explicit_implicit(options)
180
+ opts = options.dup
181
+ @explicit = opts.delete(:explicit)
182
+ @implicit = opts.delete(:implicit)
183
+ opts
184
+ end
185
+
186
+ def generate_explicit_wrapper(options)
187
+ # ExplicitWrapper is a hand-made explicit tag, but we have to use its implicit option
188
+ # to force its tag value.
189
+ @explicit_wrapper = ExplicitWrapper.new(options.merge(implicit: @explicit))
190
+ end
191
+
192
+ def generate_explicit_wrapper_options(options)
193
+ new_opts = {}
194
+ new_opts[:default] = options[:default] if options.key?(:default)
195
+ new_opts[:optional] = options[:optional] if options.key?(:optional)
196
+ new_opts
197
+ end
198
+
191
199
  def generate_implicit_element
192
200
  el = element.dup
193
201
  if el.explicit?
data/lib/rasn1.rb CHANGED
@@ -5,10 +5,14 @@ require_relative 'rasn1/errors'
5
5
  require_relative 'rasn1/types'
6
6
  require_relative 'rasn1/model'
7
7
  require_relative 'rasn1/wrapper'
8
+ require_relative 'rasn1/tracer'
8
9
 
9
10
  # Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
10
11
  # @author Sylvain Daubert
11
12
  module RASN1
13
+ # @private
14
+ CONTAINER_CLASSES = [Types::Sequence, Types::Set].freeze
15
+
12
16
  # Parse a DER/BER string without checking a model
13
17
  # @note If you want to check ASN.1 grammary, you should define a {Model}
14
18
  # and use {Model#parse}.
@@ -19,24 +23,21 @@ module RASN1
19
23
  # @param [String] der binary string to parse
20
24
  # @param [Boolean] ber if +true+, decode a BER string, else a DER one
21
25
  # @return [Types::Base]
22
- def self.parse(der, ber: false)
23
- root = nil
24
- until der.empty?
25
- type = Types.id2type(der)
26
- type.parse!(der, ber: ber)
27
- root ||= type
26
+ def self.parse(der, ber: false) # rubocop:disable Metrics:AbcSize
27
+ type = Types.id2type(der)
28
+ type.parse!(der, ber: ber)
28
29
 
29
- if [Types::Sequence, Types::Set].include? type.class
30
- subder = type.value
31
- ary = []
32
- until subder.empty?
33
- ary << self.parse(subder)
34
- subder = subder[ary.last.to_der.size..-1]
35
- end
36
- type.value = ary
30
+ if CONTAINER_CLASSES.include?(type.class)
31
+ subder = type.value
32
+ ary = []
33
+ RASN1.tracer.tracing_level += 1 unless RASN1.tracer.nil?
34
+ until subder.empty?
35
+ ary << self.parse(subder)
36
+ subder = subder[ary.last.to_der.size..-1]
37
37
  end
38
- der = der[type.to_der.size..-1]
38
+ RASN1.tracer.tracing_level -= 1 unless RASN1.tracer.nil?
39
+ type.value = ary
39
40
  end
40
- root
41
+ type
41
42
  end
42
43
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rasn1
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sylvain Daubert
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-11-12 00:00:00.000000000 Z
11
+ date: 2022-12-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: strptime
@@ -40,6 +40,7 @@ files:
40
40
  - lib/rasn1.rb
41
41
  - lib/rasn1/errors.rb
42
42
  - lib/rasn1/model.rb
43
+ - lib/rasn1/tracer.rb
43
44
  - lib/rasn1/types.rb
44
45
  - lib/rasn1/types/any.rb
45
46
  - lib/rasn1/types/base.rb