rasn2 1.0 → 1.0.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c9744f6f89db9eccc56949cfaa1be37d919f1c12cbea2178d8ffd05d6a526e72
4
- data.tar.gz: ea3fc33186e718dd636948ba6eeeb915beac90617c1736a265770daeb2a45a15
3
+ metadata.gz: 0bc40f5198815e03f0bfa923321ac614e68d4587f31b3cb18d999f40e7b49004
4
+ data.tar.gz: '09ec989aa9f0674cf2f4ed956518bd717a85de33357b7c09cd2fe6a2d2caa9b8'
5
5
  SHA512:
6
- metadata.gz: 58299aa777a6c80d63c6f8faed939ac638804a7a28a25e83e431a78391564a9138902252b0ebe1ebd7c46a168311f008c5540a23590f905d1c78598ad05bfe31
7
- data.tar.gz: 78c1496c6792b9542ab1d76c6134d12cc9cd6e8c4e55c18a62fcb5574ed6d40c7c9179cf5bf66b1f277dc9f27ee507c885fc4d5b3dbd52894d42c409d08b1206
6
+ metadata.gz: 7739ea22723954d0ee78787697e8dde59bb0b41f6fb3c23c8e34f0cab1bd23720b35e05abffb8ac389141115f598d61cf28ce32ccd7d078169cf16bf0043616c
7
+ data.tar.gz: e5ef77bb7b20f1bb6819c4935e04a302c58930bc183371b132522c0b245da9e306de8fbcb29653c535c8d6646f991ff990726466937f487a5c7ea0470034a342
@@ -1,44 +1,80 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN2
2
4
  module Helpers
3
5
  module Colorize
6
+ def colorizer
7
+ @colorizer&.first || Rainbow.new
8
+ end
9
+
10
+ def colorizer=(colorizer)
11
+ @colorizer ||= []
12
+ @colorizer << colorizer
13
+ end
14
+
15
+ def begin_colorizer(color)
16
+ @colorizer ||= []
17
+ @colorizer << Rainbow.new
18
+ @colorizer.last.enabled = color
19
+ end
20
+
21
+ def colorize(msg)
22
+ colorizer.wrap(msg)
23
+ end
24
+
25
+ def end_colorizer
26
+ @colorizer&.pop
27
+ end
28
+
4
29
  def parens_hex(input, padding=2)
5
- input = input.is_a?(Integer) ? ("0x%0#{padding}x")% input : "0x#{input}"
6
- "#{colorize('(').webgray}#{colorize(input).blue}#{colorize(')').webgray}"
30
+ value = if input.is_a?(Integer)
31
+ if input >= 0
32
+ value = "%0#{padding}X" % input
33
+ value = value.rjust((value.length + 1) / 2 * 2, '0')
34
+ "0x#{value}"
35
+ else
36
+ '0xFF'
37
+ end
38
+ else
39
+ input
40
+ end
41
+ "#{colorize('(').webgray}#{colorize(value).blue}#{colorize(')').webgray}"
7
42
  end
8
43
 
9
44
  def int_with_hex(input, raw_value=nil)
10
- if raw_value
11
- "#{colorize(input).blue} #{parens_hex(raw_value)}"
12
- else
45
+ if raw_value.nil?
13
46
  "#{colorize(input).blue} #{parens_hex(input)}"
47
+ else
48
+ "#{colorize(input).blue} #{parens_hex(raw_value)}"
14
49
  end
15
-
16
50
  end
17
51
 
18
52
  def brace_surround(input)
19
53
  "#{colorize('[').webgray} #{input} #{colorize(']').webgray}"
20
54
  end
21
55
 
22
- def colorize_class(name, tag_id=nil)
23
- if tag_id
24
- "#{colorize(name).bold.yellow} #{parens_hex(tag_id)}"
25
- else
26
- colorize(name).bold.yellow
27
- end
56
+ def colorize_class(name, tag_id=nil, *attributes)
57
+ parts = [colorize(name).bold.yellow]
58
+ parts += attributes.map { |attr| colorize_attribute(attr) } if attributes.any?
59
+ parts << parens_hex(tag_id) if tag_id
60
+ parts.join(' ')
28
61
  end
29
62
 
30
63
  def colorize_id(id, tag_class=nil)
31
64
  id = id.strip if id.is_a? String
32
65
  tag_class = tag_class.strip if tag_class.is_a? String
66
+ tag_class = '' if tag_class == 'UNIVERSAL'
67
+
68
+ return '' if id.to_s == '' && tag_class.to_s == ''
69
+
33
70
  if tag_class && tag_class != ''
34
71
  brace_surround("#{colorize_class(tag_class)} #{colorize(id).bold.green}")
35
72
  else
36
73
  brace_surround(colorize(id).bold.green)
37
74
  end
38
-
39
75
  end
40
76
 
41
- def length_specifier(data_length, encoded_length = nil)
77
+ def length_specifier(data_length, encoded_length=nil)
42
78
  encoded_length = data_length if encoded_length.nil?
43
79
  "#{colorize('len:').gray} #{colorize(data_length).blue} #{parens_hex(encoded_length)}"
44
80
  end
@@ -52,25 +88,32 @@ module RASN2
52
88
  end
53
89
 
54
90
  def colorize_default(value)
55
- colorize(value).green.bold
91
+ " #{colorize_attribute('DEFAULT VALUE')} #{colorize(value).green.bold}"
56
92
  end
57
93
 
58
- def colorize_nil(name = 'NONE')
94
+ def colorize_nil(name='NONE')
59
95
  colorize(name).red.bold
60
96
  end
61
97
 
62
98
  def colorize_enum(name, raw_value)
63
- raw_value = raw_value.unpack1('H*') if raw_value.is_a?(String)
99
+ raw_value = "0x#{raw_value.unpack1('H*').upcase}" if raw_value.is_a?(String)
64
100
  "#{colorize_class(name)} #{parens_hex(raw_value)}"
65
101
  end
66
102
 
103
+ def colorize_value(value)
104
+ colorize(value).blue
105
+ end
106
+
67
107
  def colorize_bool(value, raw_value)
68
- if value
69
- "#{colorize('TRUE').bold.green} #{parens_hex(raw_value)}"
70
- else
71
- "#{colorize('FALSE').bold.red} #{parens_hex(raw_value)}"
72
- end
108
+ hex_raw_value = raw_value.is_a?(String) ? "0x#{raw_value.unpack1('H*').upcase}" : raw_value
109
+ value = if value
110
+ colorize('TRUE').bold.green
111
+ else
112
+ colorize('FALSE').bold.red
113
+ end
114
+
115
+ "#{value} #{parens_hex(hex_raw_value)}"
73
116
  end
74
117
  end
75
118
  end
76
- end
119
+ end
data/lib/rasn2/model.rb CHANGED
@@ -129,6 +129,8 @@ module RASN2
129
129
  # @private Sequence types
130
130
  SEQUENCE_TYPES = [Types::Sequence, Types::SequenceOf, Types::Set, Types::SetOf].freeze
131
131
 
132
+ include Helpers::Colorize
133
+
132
134
  # Define helper methods to define models
133
135
  module Accel # rubocop:disable Metrics/ModuleLength
134
136
  # @return [Hash]
@@ -567,7 +569,9 @@ module RASN2
567
569
  # @return [Integer] number of parsed bytes
568
570
  # @raise [ASN1Error] error on parsing
569
571
  def parse!(der, ber: false)
570
- root.parse!(der, ber: ber)
572
+ result = root.parse!(der, ber: ber)
573
+ result.model = self if result.respond_to? :model=
574
+ result
571
575
  end
572
576
 
573
577
  # @private
@@ -634,7 +638,7 @@ module RASN2
634
638
 
635
639
  # @return [String]
636
640
  def inspect(level=0, color: true)
637
- "#{' ' * level}(#{type}) #{root.inspect(-level)}"
641
+ root.inspect(level, color: color, kind: type)
638
642
  end
639
643
 
640
644
  # Objects are equal if they have same class AND same DER
@@ -45,7 +45,7 @@ module RASN2
45
45
  }.freeze
46
46
 
47
47
  # Constructed type names
48
- CONSTRUCTED_TYPES = %w[SEQUENCE SET].freeze
48
+ CONSTRUCTED_TYPES = %w[SEQUENCE SET TAG].freeze
49
49
 
50
50
  # Error raised when parsing an ASN.1 schema fails
51
51
  class ParseError < RASN2::Error; end
data/lib/rasn2/tracer.rb CHANGED
@@ -37,7 +37,8 @@ module RASN2
37
37
  # @param [String] msg
38
38
  # @return [void]
39
39
  def trace(msg)
40
- @io.puts(indent << msg)
40
+ indented = msg.split("\n").map { |line| indent + line }.join("\n")
41
+ @io.puts(indented)
41
42
  end
42
43
 
43
44
  # Return identation for given +level+. If +nil+, use {#tracing_level}.
@@ -45,7 +46,7 @@ module RASN2
45
46
  # @return [String]
46
47
  def indent(level=nil)
47
48
  level ||= @tracing_level
48
- ' ' * level
49
+ " " * level
49
50
  end
50
51
  end
51
52
 
@@ -31,67 +31,51 @@ module RASN2
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
- total_length, _data = do_parse(der, ber: ber)
34
+ total_length, data = do_parse(der, ber: ber)
35
+ @value = data
35
36
  total_length
36
37
  end
37
38
 
38
- # @param [::Integer] level
39
- # @return [String]
40
- def inspect(level=0)
41
- str = common_inspect(level)
42
- str << if !value?
43
- 'NULL'
44
- elsif @value.is_a?(OctetString) || @value.is_a?(BitString)
45
- "#{@value.type}: #{value.value.inspect}"
46
- elsif @value.class < Base
47
- "#{@value.type}: #{value.value}"
48
- else
49
- value.to_s.inspect
50
- end
51
- end
52
-
53
39
  # @private Tracer private API
54
40
  # @return [String]
55
41
  def trace
56
42
  return trace_any if value?
57
43
 
58
- msg_type(no_id: true) << " #{colorize_nil}"
59
- end
60
-
61
- private
62
-
63
- def common_inspect(level)
64
- lvl = [0, level].max
65
- str = ' ' * lvl
66
- str << "#{@name} " unless @name.nil?
67
- str << asn1_class.to_s.upcase << ' ' unless asn1_class == :universal
68
- str << "[#{id}] EXPLICIT " if explicit?
69
- str << "[#{id}] IMPLICIT " if implicit?
70
- str << '(ANY) '
44
+ parts = []
45
+ parts << msg_type(no_id: true)
46
+ parts << colorize_nil
47
+ parts.reject(&:empty?).join(' ')
71
48
  end
72
49
 
73
50
  # @private
74
51
  # @see Types::Base#do_parse
75
52
  def do_parse(der, ber: false)
53
+ asn1_class, _pc, id, id_size = Types.decode_identifier_octets(der)
54
+ @id = id
55
+ @asn1_class = asn1_class
56
+
76
57
  if der.empty?
77
58
  return [0, ''] if optional?
78
59
 
79
60
  raise ASN1Error, 'Expected ANY but get nothing'
80
61
  end
81
62
 
82
- id_size = Types.decode_identifier_octets(der).last
83
63
  total_length, = get_data(der[id_size..], ber)
84
64
  total_length += id_size
85
65
 
86
66
  @no_value = false
87
67
  real_value = der[0, total_length].to_s
88
- @value = real_value
68
+ @value = if constructed?
69
+ RASN2.parse(real_value, ber: ber)
70
+ else
71
+ real_value
72
+ end
89
73
 
90
- [total_length, real_value]
74
+ [total_length, @value]
91
75
  end
92
76
 
93
77
  def trace_any
94
- msg_type(no_id: true) << trace_data(value)
78
+ "#{msg_type(no_id: false)}#{trace_data(value)}"
95
79
  end
96
80
  end
97
81
  end
@@ -45,6 +45,10 @@ module RASN2
45
45
  # ctype_implicit = RASN2::Types::Integer.new(implicit: 0)
46
46
  # @author Sylvain Daubert
47
47
  class Base # rubocop:disable Metrics/ClassLength
48
+ ID = nil
49
+
50
+ attr_accessor :model
51
+
48
52
  # Allowed ASN.1 classes
49
53
  CLASSES = {
50
54
  universal: 0x00,
@@ -54,7 +58,7 @@ module RASN2
54
58
  }.freeze
55
59
 
56
60
  def colorize(msg)
57
- tracer ? tracer.colorize(msg) : Rainbow.new.wrap(msg)
61
+ tracer ? tracer.colorize(msg) : self.colorizer.wrap(msg)
58
62
  end
59
63
 
60
64
  include RASN2::Helpers::Colorize
@@ -199,6 +203,10 @@ module RASN2
199
203
  defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
200
204
  end
201
205
 
206
+ def private?
207
+ self.instance_of?(Types::Tag)
208
+ end
209
+
202
210
  # @abstract This method SHOULD be partly implemented by subclasses, which
203
211
  # SHOULD respond to +#value_to_der+.
204
212
  # @return [String] DER-formated string
@@ -254,14 +262,43 @@ module RASN2
254
262
  value_to_der.size
255
263
  end
256
264
 
265
+ def attributes
266
+ attributes = []
267
+ attributes << 'OPTIONAL' if optional?
268
+ attributes << 'CONSTRUCTED' if constructed? && !(self.class < Constructed)
269
+ attributes << 'EXPLICIT' if explicit?
270
+ attributes << 'IMPLICIT' if implicit?
271
+ attributes
272
+ end
273
+
257
274
  # @param [Integer] level
258
275
  # @return [String]
259
- def inspect(level=0, color: true)
260
- str = common_inspect(level)
261
- str << ' ' << inspect_value
262
- str << ' OPTIONAL' if optional?
263
- str << " DEFAULT #{@default}" unless @default.nil?
264
- str
276
+ def inspect(level=0, color: false)
277
+ parts = []
278
+ new_level = level.abs + 1
279
+
280
+ begin_colorizer(color)
281
+ parts << common_inspect(level, color: color)
282
+
283
+ if constructed?
284
+ if @value.is_a?(Array)
285
+ @value.each do |value|
286
+ parts << "\n"
287
+ parts << value.inspect(new_level, color: color).to_s
288
+ end
289
+ else
290
+ parts << "\n"
291
+ if @value.is_a?(Base)
292
+ parts << @value.inspect(new_level, color: color).to_s
293
+ else
294
+ @value.inspect.to_s
295
+ end
296
+ end
297
+ else
298
+ parts << inspect_value unless inspect_value.empty?
299
+ end
300
+ end_colorizer
301
+ parts.reject(&:empty?).join(' ')
265
302
  end
266
303
 
267
304
  # Objects are equal if they have same class AND same DER
@@ -303,12 +340,14 @@ module RASN2
303
340
  def trace
304
341
  return trace_real if value?
305
342
 
306
- msg = msg_type
307
- if default.nil? # rubocop:disable Style/ConditionalAssignment
308
- msg << " #{colorize_nil}"
309
- else
310
- msg << " #{colorize_attribute('DEFAULT VALUE')} #{colorize_default(value)}"
311
- end
343
+ parts = [msg_type]
344
+ parts << if default.nil?
345
+ colorize_nil
346
+ else
347
+ colorize_default(value)
348
+ end
349
+
350
+ parts.reject(&:empty?).join(' ')
312
351
  end
313
352
 
314
353
  # @private Parse tage and length from binary string. Return data length and binary data.
@@ -346,42 +385,28 @@ module RASN2
346
385
  @value = der
347
386
  end
348
387
 
349
- private
350
-
351
388
  def trace_real
352
- encoded_id = unpack(encode_identifier_octets)
389
+ raw_id = encode_identifier_octets
390
+ encoded_id = raw_id.unpack1('w*')
353
391
  data_length = raw_data.length
354
- encoded_length = unpack(raw_length)
355
- msg = "#{msg_type} #{parens_hex(encoded_id)}, #{length_specifier(data_length, encoded_length)}"
392
+ encoded_length = raw_length.unpack1('w*')
393
+ parts = [msg_type(raw_id.unpack1('H*').to_i(16), encoded_id)]
394
+ msg = parts.reject(&:empty?).join(' ')
395
+ msg << ", #{length_specifier(data_length, encoded_length)}"
356
396
  msg << trace_data
357
397
  end
358
398
 
359
399
  def trace_data(data=nil)
360
- byte_count = 0
361
400
  data ||= raw_data
362
-
363
- lines = []
364
- str = ''
365
- data.each_byte do |byte|
366
- if (byte_count % 16).zero?
367
- str = trace_format_new_data_line(byte_count)
368
- lines << str
369
- end
370
- str[compute_trace_index(byte_count, 3), 2] = '%02x' % byte
371
- str[compute_trace_index(byte_count, 1, 49)] = byte.between?(32, 126) ? byte.chr : '.'
372
- byte_count += 1
401
+ format_string = data.size > 65_535 ? '%08X' : '%04X'
402
+ lines = data.bytes.each_slice(16).map.with_index do |chunk, index|
403
+ offset = format_string % (index * 16)
404
+ hex = chunk.map { |b| '%02x' % b }.join(' ')
405
+ hex_padded = hex.ljust(47) # 16*2 hex digits + 15 spaces
406
+ ascii = chunk.map { |b| (32..126).cover?(b) ? b.chr : '.' }.join
407
+ " #{offset} #{hex_padded} |#{ascii}|"
373
408
  end
374
- lines.map(&:rstrip).join << "\n"
375
- end
376
-
377
- def trace_format_new_data_line(count)
378
- head_line = RASN2.tracer.indent(RASN2.tracer.tracing_level + 1)
379
- ("\n#{head_line}%04x " % count) << ' ' * 68
380
- end
381
-
382
- def compute_trace_index(byte_count, byte_count_mul=1, offset=0)
383
- base_idx = 7 + RASN2.tracer.indent(RASN2.tracer.tracing_level + 1).length
384
- base_idx + offset + (byte_count % 16) * byte_count_mul
409
+ "\n#{lines.join("\n")}"
385
410
  end
386
411
 
387
412
  def unpack(binstr)
@@ -389,22 +414,16 @@ module RASN2
389
414
  end
390
415
 
391
416
  def asn1_class_to_s
392
- asn1_class == :universal ? '' : asn1_class.to_s.upcase << ' '
417
+ asn1_class.to_s.upcase
393
418
  end
394
419
 
395
- def msg_type(no_id: false)
396
- msg = name.nil? ? +'' : "#{colorize_name(name)} "
397
- msg << "#{colorize_id(id, asn1_class_to_s)} " unless no_id
398
- msg << if explicit?
399
- + "#{colorize_attribute('EXPLICIT')} "
400
- elsif implicit?
401
- + "#{colorize_attribute('IMPLICIT')} "
402
- else
403
- +''
404
- end
405
- msg << colorize_class(type)
406
- msg << " #{colorize_attribute('OPTIONAL')}" if optional?
407
- msg
420
+ def msg_type(raw_id=nil, _encoded_id=nil, no_id: false)
421
+ parts = []
422
+ parts << colorize_name(name) unless name.nil?
423
+ real_id = no_id ? nil : raw_id || id
424
+ parts << colorize_id(id, asn1_class_to_s) if real_id
425
+ parts << colorize_class(type, real_id, *self.attributes)
426
+ parts.reject(&:empty?).join(' ')
408
427
  end
409
428
 
410
429
  def pc_bit
@@ -417,22 +436,32 @@ module RASN2
417
436
  end
418
437
  end
419
438
 
420
- def common_inspect(level)
421
- lvl = [level, 0].max
422
- str = ' ' * lvl
423
- str << "#{@name} " unless @name.nil?
424
- str << asn1_class_to_s
425
- str << "[#{id}] EXPLICIT " if explicit?
426
- str << "[#{id}] IMPLICIT " if implicit?
427
- str << "#{type}:"
439
+ def universal?
440
+ @asn1_class == :universal
441
+ end
442
+
443
+ def common_inspect(level=0, color: false)
444
+ begin_colorizer(color)
445
+ parts = []
446
+ parts << "(#{model})" if model
447
+ parts << colorize_name(name) if name
448
+ parts << colorize_id(id, asn1_class_to_s) if id && !universal?
449
+ class_name = colorize_class(type, nil, *self.attributes)
450
+ class_name += ':' if constructed?
451
+ parts << class_name
452
+ end_colorizer
453
+ "#{" " * level}" + parts.reject(&:empty?).join(' ').to_s
428
454
  end
429
455
 
430
456
  def inspect_value
431
- if value?
432
- value.inspect
433
- else
434
- '(NO VALUE)'
435
- end
457
+ str = if value?
458
+ colorize_value(value.inspect)
459
+ else
460
+ colorize_nil('(NO VALUE)')
461
+ end
462
+
463
+ str += colorize_default(@default) if @default
464
+ str
436
465
  end
437
466
 
438
467
  def value_to_der
@@ -586,6 +615,8 @@ module RASN2
586
615
  elsif !@default.nil?
587
616
  @value = @default
588
617
  else
618
+ return true if self.instance_of?(Tag)
619
+
589
620
  raise_id_error(der)
590
621
  end
591
622
  false
@@ -638,18 +669,24 @@ module RASN2
638
669
 
639
670
  def raise_id_error(der)
640
671
  msg = name.nil? ? +'' : "#{name}: "
641
- msg << "Expected #{self2name} but get #{der2name(der)}"
672
+ msg << "Expected #{self2name} but got #{der2name(der)}"
642
673
 
643
674
  raise ASN1Error, msg
644
675
  end
645
676
 
646
677
  def self2name
647
- name = "#{colorize_class(asn1_class.to_s.upcase)} #{colorize_attribute(constructed? ? 'CONSTRUCTED' : 'PRIMITIVE')}"
678
+ parts = [asn1_class.to_s.upcase]
679
+ parts << (constructed? ? 'CONSTRUCTED' : 'PRIMITIVE').to_s
648
680
  if implicit? || explicit?
649
- name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
681
+ parts << '0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
650
682
  else
651
- name << ' ' << colorize_class(self.class.type)
683
+ parts << self.class.type.to_s
652
684
  end
685
+ parts.reject(&:empty?).join(' ')
686
+ end
687
+
688
+ def indent(input, by=2)
689
+ input.split("\n").map { |line| (" " * by) + line }.join("\n")
653
690
  end
654
691
 
655
692
  def der2name(der)
@@ -658,11 +695,12 @@ module RASN2
658
695
  asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
659
696
  name = "#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
660
697
  type = find_type(id)
661
- name << " #{type.nil? ? "#{colorize(bin2hex(der[0...id_size])).magenta}" : colorize(type.encoded_type).green}"
698
+ name << " #{type.nil? ? bin2hex(der[0...id_size]).to_s : type.encoded_type}"
662
699
  end
663
700
 
664
701
  def find_type(id)
665
702
  Types.constants.map { |c| Types.const_get(c) }
703
+ .grep(Class)
666
704
  .select { |klass| klass < Primitive || klass < Constructed }
667
705
  .find { |klass| id == klass::ID }
668
706
  end
@@ -34,11 +34,8 @@ module RASN2
34
34
  end
35
35
  end
36
36
 
37
- # @param [Integer] level
38
- # @return [String]
39
- def inspect(level=0)
40
- str = common_inspect(level)
41
- str << " #{value.inspect} (bit length: #{bit_length})"
37
+ def inspect_value
38
+ "(bit length: #{bit_length}): #{value.inspect}"
42
39
  end
43
40
 
44
41
  # Same as {Base#can_build?} but also check bit_length
@@ -50,7 +50,7 @@ module RASN2
50
50
  def trace_data
51
51
  return super if explicit?
52
52
 
53
- ' ' + colorize_bool(raw_data != "\x00".b, raw_data.unpack1('H*'))
53
+ " #{colorize_bool(raw_data != "\x00".b, raw_data)}"
54
54
  end
55
55
  end
56
56
  end
@@ -126,12 +126,12 @@ module RASN2
126
126
 
127
127
  # @param [::Integer] level
128
128
  # @return [String]
129
- def inspect(level=0)
129
+ def inspect(level=0, color: true)
130
130
  str = common_inspect(level)
131
131
  str << if defined?(@chosen) && value?
132
- "\n#{@value[@chosen].inspect(level + 1)}"
132
+ @value[@chosen].inspect(level + 1)
133
133
  else
134
- ' not chosen!'
134
+ 'not chosen!'
135
135
  end
136
136
  end
137
137
 
@@ -23,29 +23,6 @@ module RASN2
23
23
  (el.value.respond_to?(:empty?) ? !el.value.empty? : !el.value.nil?))
24
24
  end
25
25
  end
26
-
27
- # @param [::Integer] level (default: 0)
28
- # @return [String]
29
- def inspect(level=0)
30
- case @value
31
- when Array
32
- str = common_inspect(level)
33
- str << "\n"
34
- level = level.abs + 1
35
- @value.each do |item|
36
- case item
37
- when Base, Model, Wrapper
38
- str << "#{item.inspect(level)}\n"
39
- else
40
- str << ' ' * level
41
- str << "#{item.inspect}\n"
42
- end
43
- end
44
- str
45
- else
46
- super
47
- end
48
- end
49
26
  end
50
27
  end
51
28
  end
@@ -16,6 +16,10 @@ module RASN2
16
16
  def self.type
17
17
  'IA5String'
18
18
  end
19
+
20
+ def inspect_value
21
+ colorize_value(@value.inspect)
22
+ end
19
23
  end
20
24
  end
21
25
  end
@@ -155,8 +155,9 @@ module RASN2
155
155
 
156
156
  def trace_data
157
157
  return super if explicit?
158
+
158
159
  i = der_to_int_value(raw_data)
159
- return " #{int_with_hex(i, raw_data.unpack1('H*'))}" if @enum.empty?
160
+ return " #{int_with_hex(i, raw_data.unpack1('w*'))}" if @enum.empty?
160
161
 
161
162
  v = int_to_enum(i, check_enum: false)
162
163
  " #{colorize_enum(v, raw_data)}"
@@ -9,10 +9,11 @@ module RASN2
9
9
  ID = 0x05
10
10
 
11
11
  # @return [String]
12
- def inspect(level=0)
13
- str = common_inspect(level)[0..-2] # remove terminal ':'
14
- str << ' OPTIONAL' if optional?
15
- str
12
+ def inspect(level=0, color: true)
13
+ begin_colorizer(color)
14
+ result = common_inspect(level)
15
+ end_colorizer
16
+ result
16
17
  end
17
18
 
18
19
  # @return [Boolean]
@@ -15,11 +15,8 @@ module RASN2
15
15
  # OctetString id value
16
16
  ID = 4
17
17
 
18
- # @param [::Integer] level
19
- # @return [String]
20
- def inspect(level=0)
21
- str = common_inspect(level)
22
- str << " #{value.inspect}"
18
+ def inspect_value
19
+ value.inspect
23
20
  end
24
21
 
25
22
  # Make string value from +der+ string. Force encoding to UTF-8
@@ -107,22 +107,17 @@ module RASN2
107
107
 
108
108
  # @param [::Integer] level
109
109
  # @return [String]
110
- def inspect(level=0)
111
- str = common_inspect(level)
112
- str << "\n"
113
- level = level.abs + 1
114
- @value.each do |item|
115
- case item
116
- when Base, Model
117
- next if item.optional? && item.value.nil?
118
-
119
- str << item.inspect(level)
120
- str << "\n" unless str.end_with?("\n")
121
- else
122
- str << (' ' * level) << "#{item.inspect}\n"
123
- end
110
+ def inspect(level=0, color: false)
111
+ new_level = level.abs + 1
112
+ begin_colorizer(color)
113
+ name = @name ? "#{colorize_name(@name)} " : ''
114
+ lines = []
115
+ lines << "#{" " * level}#{name}SEQUENCE OF:"
116
+ lines += @value.map do |item|
117
+ item.inspect(new_level, color: color)
124
118
  end
125
- str
119
+ end_colorizer
120
+ lines.join("\n")
126
121
  end
127
122
 
128
123
  # Make sequence of value from +der+ string
@@ -24,7 +24,7 @@ module RASN2
24
24
  # generic = Tag.new(tag: :private, value: [Integer.new, OctetString.new])
25
25
  # # or equivalently
26
26
  # generic = Tag.new(any_private: true, value: [Integer.new])
27
- class Tag < Constructed
27
+ class Tag < Base
28
28
  # Placeholder ID — not used directly; tag is always dynamic.
29
29
  ID = 0
30
30
 
@@ -72,22 +72,6 @@ module RASN2
72
72
  end
73
73
  end
74
74
 
75
- # Parse constructed content from DER
76
- # @param [String] der
77
- # @param [Boolean] ber
78
- # @return [void]
79
- def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
80
- if @value.is_a?(Array) && !@value.empty?
81
- nb_bytes = 0
82
- @value.each do |element|
83
- nb_bytes += element.parse!(der[nb_bytes..])
84
- end
85
- else
86
- @value = der
87
- der.length
88
- end
89
- end
90
-
91
75
  private
92
76
 
93
77
  def extract_tag_binding(options)
@@ -114,6 +98,12 @@ module RASN2
114
98
  end
115
99
 
116
100
  def check_id(der)
101
+ asn1_class, pc, tag_id, size = Types.decode_identifier_octets(der)
102
+ @asn1_class = asn1_class
103
+ @constructed = pc
104
+ @tag = tag_id
105
+ @tag_size = size
106
+
117
107
  return check_any_private_id(der) if @any_private && @accepted_tags.empty?
118
108
  return check_multi_tag_id(der) if @accepted_tags.length > 1
119
109
 
@@ -123,10 +113,7 @@ module RASN2
123
113
  def check_any_private_id(der)
124
114
  return no_match(der) if der.nil? || der.empty?
125
115
 
126
- first_octet = der.unpack1('C').to_i
127
- asn1_class_bits = first_octet & CLASS_MASK
128
- if asn1_class_bits == CLASSES[:private]
129
- _, _, tag_id, = Types.decode_identifier_octets(der)
116
+ if asn1_class == :private
130
117
  @id_value = tag_id
131
118
  @matched_tag = tag_id
132
119
  true
@@ -164,15 +151,13 @@ module RASN2
164
151
 
165
152
  def value_to_der
166
153
  case @value
167
- when Array then @value.map(&:to_der).join
168
- else @value.to_s
154
+ when Array
155
+ @value.map(&:to_der).join
156
+ else
157
+ @value.to_s
169
158
  end
170
159
  end
171
160
 
172
- def trace_data
173
- ''
174
- end
175
-
176
161
  def explicit_type
177
162
  opts = { name: name, value: @value }
178
163
  if @accepted_tags.length == 1
data/lib/rasn2/types.rb CHANGED
@@ -15,6 +15,7 @@ module RASN2
15
15
  return @primitives unless @primitives.empty?
16
16
 
17
17
  @primitives = self.constants.map { |c| Types.const_get(c) }
18
+ .grep(Class)
18
19
  .select { |klass| klass < Primitive }
19
20
  end
20
21
 
@@ -24,6 +25,7 @@ module RASN2
24
25
  return @constructed unless @constructed.empty?
25
26
 
26
27
  @constructed = self.constants.map { |c| Types.const_get(c) }
28
+ .grep(Class)
27
29
  .select { |klass| klass < Constructed }
28
30
  end
29
31
 
@@ -67,10 +69,10 @@ module RASN2
67
69
  asn1class, pc, id, = self.decode_identifier_octets(der)
68
70
  # cache_id: check versus class and 5 LSB bits
69
71
  cache_id = der.unpack1('C') & 0xdf
70
- klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::Base
72
+ klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::Tag
71
73
  is_constructed = (pc == :constructed)
72
74
  options = { class: asn1class, constructed: is_constructed }
73
- options[:tag_value] = id if klass == Types::Base
75
+ options[:tag_value] = id if klass == Types::Tag
74
76
  klass.new(options)
75
77
  end
76
78
 
data/lib/rasn2/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module RASN2
4
4
  # RASN2 version number
5
- VERSION = '1.0'
5
+ VERSION = '1.0.1'
6
6
  end
data/lib/rasn2/wrapper.rb CHANGED
@@ -201,13 +201,6 @@ module RASN2
201
201
  !constructed?
202
202
  end
203
203
 
204
- # @param [::Integer] level
205
- # @return [String]
206
- def inspect(level=0)
207
- return super() unless explicit?
208
-
209
- @explicit_wrapper.inspect(level) << ' ' << super()
210
- end
211
204
 
212
205
  private
213
206
 
data/lib/rasn2.rb CHANGED
@@ -35,7 +35,7 @@ module RASN2
35
35
  type = Types.id2type(der)
36
36
  size = type.parse!(der, ber: ber)
37
37
 
38
- if CONTAINER_CLASSES.include?(type.class)
38
+ if CONTAINER_CLASSES.include?(type.class) || type.constructed?
39
39
  RASN2.tracer.tracing_level += 1 unless RASN2.tracer.nil?
40
40
  content = self.parse(type.value)
41
41
  type.value = content.is_a?(Array) ? content : [content]
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rasn2
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.0'
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - LemonTree55