rasn2 0.16.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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/lib/{rasn1 → rasn2}/errors.rb +2 -2
  3. data/lib/rasn2/helpers/colorize.rb +119 -0
  4. data/lib/{rasn1 → rasn2}/model.rb +16 -12
  5. data/lib/{rasn1 → rasn2}/schema_parser.rb +10 -10
  6. data/lib/{rasn1 → rasn2}/tracer.rb +15 -14
  7. data/lib/{rasn1 → rasn2}/types/any.rb +18 -34
  8. data/lib/{rasn1 → rasn2}/types/base.rb +120 -82
  9. data/lib/{rasn1 → rasn2}/types/bit_string.rb +3 -6
  10. data/lib/{rasn1 → rasn2}/types/bmp_string.rb +1 -1
  11. data/lib/{rasn1 → rasn2}/types/boolean.rb +2 -2
  12. data/lib/{rasn1 → rasn2}/types/choice.rb +5 -5
  13. data/lib/{rasn1 → rasn2}/types/constrained.rb +2 -2
  14. data/lib/{rasn1 → rasn2}/types/constructed.rb +1 -24
  15. data/lib/{rasn1 → rasn2}/types/enumerated.rb +3 -3
  16. data/lib/{rasn1 → rasn2}/types/generalized_time.rb +1 -1
  17. data/lib/{rasn1 → rasn2}/types/ia5_string.rb +5 -1
  18. data/lib/{rasn1 → rasn2}/types/integer.rb +3 -2
  19. data/lib/{rasn1 → rasn2}/types/null.rb +6 -5
  20. data/lib/{rasn1 → rasn2}/types/numeric_string.rb +1 -1
  21. data/lib/{rasn1 → rasn2}/types/object_id.rb +1 -1
  22. data/lib/{rasn1 → rasn2}/types/octet_string.rb +3 -6
  23. data/lib/{rasn1 → rasn2}/types/primitive.rb +1 -1
  24. data/lib/{rasn1 → rasn2}/types/printable_string.rb +1 -1
  25. data/lib/{rasn1 → rasn2}/types/sequence.rb +6 -6
  26. data/lib/{rasn1 → rasn2}/types/sequence_of.rb +20 -25
  27. data/lib/{rasn1 → rasn2}/types/set.rb +5 -5
  28. data/lib/{rasn1 → rasn2}/types/set_of.rb +1 -1
  29. data/lib/{rasn1 → rasn2}/types/tag.rb +13 -28
  30. data/lib/{rasn1 → rasn2}/types/universal_string.rb +1 -1
  31. data/lib/{rasn1 → rasn2}/types/utc_time.rb +1 -1
  32. data/lib/{rasn1 → rasn2}/types/utf8_string.rb +1 -1
  33. data/lib/{rasn1 → rasn2}/types/visible_string.rb +1 -1
  34. data/lib/{rasn1 → rasn2}/types.rb +7 -5
  35. data/lib/{rasn1 → rasn2}/value_notation.rb +5 -5
  36. data/lib/rasn2/version.rb +6 -0
  37. data/lib/{rasn1 → rasn2}/wrapper.rb +8 -15
  38. data/lib/{rasn1.rb → rasn2.rb} +14 -14
  39. metadata +40 -40
  40. data/lib/rasn1/helpers/colorize.rb +0 -76
  41. data/lib/rasn1/version.rb +0 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b52ab2b6b1a8019f3ae996d875ef09869cf715c2a58cce73c58bd8ba6a50b95
4
- data.tar.gz: ebd0936fdb3a8921c97c33d18b2938bd461fd525a6991dd52a45b65be552bb2c
3
+ metadata.gz: 0bc40f5198815e03f0bfa923321ac614e68d4587f31b3cb18d999f40e7b49004
4
+ data.tar.gz: '09ec989aa9f0674cf2f4ed956518bd717a85de33357b7c09cd2fe6a2d2caa9b8'
5
5
  SHA512:
6
- metadata.gz: 3e57fb8bf6ebd8e2d9df9558613cac061f8f73344ee6fcf73ae72abb0946313503a6f4df24d5487a2859fd51c4dc9d4d74391570adaddd7b33ec2ce1d93ac3ca
7
- data.tar.gz: e5d1dc4e78a2e7d4bd1830ae1f0bc7d4eae17aaccdbf6708f42ef7cacc3ff6719ff83cb6d9f732971534199b18b533343f1ba16135229870673c308ce18f6267
6
+ metadata.gz: 7739ea22723954d0ee78787697e8dde59bb0b41f6fb3c23c8e34f0cab1bd23720b35e05abffb8ac389141115f598d61cf28ce32ccd7d078169cf16bf0043616c
7
+ data.tar.gz: e5ef77bb7b20f1bb6819c4935e04a302c58930bc183371b132522c0b245da9e306de8fbcb29653c535c8d6646f991ff990726466937f487a5c7ea0470034a342
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RASN1
3
+ module RASN2
4
4
  # Base error class
5
5
  class Error < StandardError; end
6
6
 
@@ -19,7 +19,7 @@ module RASN1
19
19
  class EnumeratedError < Error; end
20
20
 
21
21
  # CHOICE error: {Types::Choice#chosen} not set
22
- class ChoiceError < RASN1::Error
22
+ class ChoiceError < RASN2::Error
23
23
  # @param [Types::Base] object
24
24
  def initialize(object)
25
25
  @object = object
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN2
4
+ module Helpers
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
+
29
+ def parens_hex(input, padding=2)
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}"
42
+ end
43
+
44
+ def int_with_hex(input, raw_value=nil)
45
+ if raw_value.nil?
46
+ "#{colorize(input).blue} #{parens_hex(input)}"
47
+ else
48
+ "#{colorize(input).blue} #{parens_hex(raw_value)}"
49
+ end
50
+ end
51
+
52
+ def brace_surround(input)
53
+ "#{colorize('[').webgray} #{input} #{colorize(']').webgray}"
54
+ end
55
+
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(' ')
61
+ end
62
+
63
+ def colorize_id(id, tag_class=nil)
64
+ id = id.strip if id.is_a? String
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
+
70
+ if tag_class && tag_class != ''
71
+ brace_surround("#{colorize_class(tag_class)} #{colorize(id).bold.green}")
72
+ else
73
+ brace_surround(colorize(id).bold.green)
74
+ end
75
+ end
76
+
77
+ def length_specifier(data_length, encoded_length=nil)
78
+ encoded_length = data_length if encoded_length.nil?
79
+ "#{colorize('len:').gray} #{colorize(data_length).blue} #{parens_hex(encoded_length)}"
80
+ end
81
+
82
+ def colorize_name(name)
83
+ colorize(name).bold.white
84
+ end
85
+
86
+ def colorize_attribute(attribute)
87
+ colorize(attribute).bold.magenta
88
+ end
89
+
90
+ def colorize_default(value)
91
+ " #{colorize_attribute('DEFAULT VALUE')} #{colorize(value).green.bold}"
92
+ end
93
+
94
+ def colorize_nil(name='NONE')
95
+ colorize(name).red.bold
96
+ end
97
+
98
+ def colorize_enum(name, raw_value)
99
+ raw_value = "0x#{raw_value.unpack1('H*').upcase}" if raw_value.is_a?(String)
100
+ "#{colorize_class(name)} #{parens_hex(raw_value)}"
101
+ end
102
+
103
+ def colorize_value(value)
104
+ colorize(value).blue
105
+ end
106
+
107
+ def colorize_bool(value, raw_value)
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)}"
116
+ end
117
+ end
118
+ end
119
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RASN1
3
+ module RASN2
4
4
  # @abstract
5
5
  # {Model} class is a base class to define ASN.1 models.
6
6
  # == Create a simple ASN.1 model
@@ -11,7 +11,7 @@ module RASN1
11
11
  # house [1] EXPLICIT INTEGER DEFAULT 0
12
12
  # }
13
13
  # you may create your model like this:
14
- # class Record < RASN1::Model
14
+ # class Record < RASN2::Model
15
15
  # sequence(:record,
16
16
  # content: [integer(:id),
17
17
  # integer(:room, implicit: 0, optional: true),
@@ -20,7 +20,7 @@ module RASN1
20
20
  #
21
21
  # Since 0.17.0, content may also be defined through a block. This is strictly
22
22
  # equivalent to the +:content+ option:
23
- # class Record < RASN1::Model
23
+ # class Record < RASN2::Model
24
24
  # sequence :record do
25
25
  # integer :id
26
26
  # integer :room, implicit: 0, optional: true
@@ -28,7 +28,7 @@ module RASN1
28
28
  # end
29
29
  # end
30
30
  # Blocks may be nested, and may use all model helpers (+#model+, +#wrapper+, +#sequence_of+, ...):
31
- # class PersonnelRecord < RASN1::Model
31
+ # class PersonnelRecord < RASN2::Model
32
32
  # sequence :personnelRecord do
33
33
  # utf8_string :name
34
34
  # utf8_string :title
@@ -43,7 +43,7 @@ module RASN1
43
43
  #
44
44
  # === Parse a DER-encoded string
45
45
  # record = Record.parse(der_string)
46
- # record[:id] # => RASN1::Types::Integer
46
+ # record[:id] # => RASN2::Types::Integer
47
47
  # record[:id].value # => Integer
48
48
  # record[:id].to_i # => Integer
49
49
  # record[:id].asn1_class # => Symbol
@@ -61,7 +61,7 @@ module RASN1
61
61
  #
62
62
  # == Create a more complex model
63
63
  # Models may be nested. For example:
64
- # class Record2 < RASN1::Model
64
+ # class Record2 < RASN2::Model
65
65
  # sequence(:record2,
66
66
  # content: [boolean(:rented, default: false),
67
67
  # model(:a_record, Record)])
@@ -74,7 +74,7 @@ module RASN1
74
74
  # or like this:
75
75
  # record2 = Record2.new(rented: true, a_record: { id: 65537, room: 43 })
76
76
  # Same model, using a block:
77
- # class Record2 < RASN1::Model
77
+ # class Record2 < RASN2::Model
78
78
  # sequence :record2 do
79
79
  # boolean :rented, default: false
80
80
  # model :a_record, Record
@@ -129,6 +129,8 @@ module RASN1
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]
@@ -193,7 +195,7 @@ module RASN1
193
195
 
194
196
  # Update options of root element.
195
197
  # May be used when subclassing.
196
- # class Model1 < RASN1::Model
198
+ # class Model1 < RASN2::Model
197
199
  # sequence :seq, implicit: 0,
198
200
  # content: [bool(:bool), integer(:int)]
199
201
  # end
@@ -567,7 +569,9 @@ module RASN1
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
@@ -587,10 +591,10 @@ module RASN1
587
591
  # @return [Object,nil]
588
592
  # @return [Object,nil]
589
593
  # @example
590
- # class MyModel1 < RASN1::Model
594
+ # class MyModel1 < RASN2::Model
591
595
  # sequence('seq', content: [boolean('boolean'), integer('int')])
592
596
  # end
593
- # class MyModel2 < RASN1::Model
597
+ # class MyModel2 < RASN2::Model
594
598
  # sequence('root', content: [sequence_of('list', MyModel1)])
595
599
  # end
596
600
  # model = MyModel2.new
@@ -634,7 +638,7 @@ module RASN1
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
@@ -1,13 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RASN1
3
+ module RASN2
4
4
  # Parser for ASN.1 text schema definitions.
5
5
  #
6
6
  # Parses ASN.1 module definitions and generates {Model} subclasses from them.
7
7
  #
8
8
  # @example Parse a schema file
9
- # models = RASN1::SchemaParser.parse_file('path/to/schema.asn')
10
- # # => { 'PersonnelRecord' => PersonnelRecord (class < RASN1::Model) }
9
+ # models = RASN2::SchemaParser.parse_file('path/to/schema.asn')
10
+ # # => { 'PersonnelRecord' => PersonnelRecord (class < RASN2::Model) }
11
11
  #
12
12
  # @example Parse a schema string
13
13
  # schema = <<~ASN1
@@ -19,12 +19,12 @@ module RASN1
19
19
  # }
20
20
  # END
21
21
  # ASN1
22
- # models = RASN1::SchemaParser.parse(schema)
22
+ # models = RASN2::SchemaParser.parse(schema)
23
23
  #
24
24
  # @author rickmark
25
25
  # @since 0.16.0
26
26
  module SchemaParser
27
- # ASN.1 type name to RASN1 type DSL method mapping
27
+ # ASN.1 type name to RASN2 type DSL method mapping
28
28
  TYPE_MAP = {
29
29
  'BOOLEAN' => :boolean,
30
30
  'INTEGER' => :integer,
@@ -45,10 +45,10 @@ module RASN1
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
- class ParseError < RASN1::Error; end
51
+ class ParseError < RASN2::Error; end
52
52
 
53
53
  class << self
54
54
  # Parse an ASN.1 schema from a file and generate Model classes.
@@ -348,7 +348,7 @@ module RASN1
348
348
  # Build a single Model subclass
349
349
  # @return [Class]
350
350
  def build_model_class(type_name, type_def, mod, existing_models, namespace)
351
- klass = Class.new(RASN1::Model)
351
+ klass = Class.new(RASN2::Model)
352
352
 
353
353
  root_name = type_name.gsub(/([a-z])([A-Z])/, '\1_\2').gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').downcase.to_sym
354
354
 
@@ -445,14 +445,14 @@ module RASN1
445
445
  # (end of private section)
446
446
  end
447
447
 
448
- # Convert an ASN.1 type name to the corresponding RASN1 Model DSL method symbol
448
+ # Convert an ASN.1 type name to the corresponding RASN2 Model DSL method symbol
449
449
  # @param [String] type_name
450
450
  # @return [Symbol, nil]
451
451
  def self.type_to_method(type_name)
452
452
  TYPE_MAP[type_name]
453
453
  end
454
454
 
455
- # Convert an ASN.1 type name to the corresponding RASN1::Types class
455
+ # Convert an ASN.1 type name to the corresponding RASN2::Types class
456
456
  # @param [String] type_name
457
457
  # @return [Class, nil]
458
458
  def self.type_to_class(type_name)
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'rainbow'
4
4
 
5
- module RASN1
5
+ module RASN2
6
6
  # @private
7
7
  class Tracer
8
8
  # @return [IO]
@@ -37,7 +37,8 @@ module RASN1
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,23 +46,23 @@ module RASN1
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
 
52
- # Trace RASN1 parsing to +io+.
53
+ # Trace RASN2 parsing to +io+.
53
54
  # All parsing methods called in block are traced to +io+. Each ASN.1 element is
54
55
  # traced in a line showing element's id, its length and its data.
55
56
  # @param [IO] io
56
57
  # @param [Boolean] color enable colorized output using pastel gem (requires pastel to be installed)
57
58
  # @example
58
- # RASN1.trace do
59
- # RASN1.parse("\x02\x01\x01") # puts "INTEGER id: 2 (0x02), len: 1 (0x01), data: 0x01"
59
+ # RASN2.trace do
60
+ # RASN2.parse("\x02\x01\x01") # puts "INTEGER id: 2 (0x02), len: 1 (0x01), data: 0x01"
60
61
  # end
61
- # RASN1.parse("\x01\x01\xff") # puts nothing onto STDOUT
62
+ # RASN2.parse("\x01\x01\xff") # puts nothing onto STDOUT
62
63
  # @example with color
63
- # RASN1.trace(color: true) do
64
- # RASN1.parse("\x02\x01\x01") # same output but with ANSI colors
64
+ # RASN2.trace(color: true) do
65
+ # RASN2.parse("\x02\x01\x01") # same output but with ANSI colors
65
66
  # end
66
67
  # @return [void]
67
68
  def self.trace(io=$stdout, color: false)
@@ -142,7 +143,7 @@ module RASN1
142
143
  # Parse +der+ with tracing abillity
143
144
  # @see #parse!
144
145
  def parse_with_tracing(der, ber: false)
145
- RASN1.tracer.trace(self.trace)
146
+ RASN2.tracer.trace(self.trace)
146
147
  parse_without_tracing(der, ber: ber)
147
148
  end
148
149
  end
@@ -166,9 +167,9 @@ module RASN1
166
167
  # @private
167
168
  # der_to_value +der+ with tracing abillity
168
169
  def der_to_value_with_tracing(der, ber: false)
169
- RASN1.tracer.tracing_level += 1
170
+ RASN2.tracer.tracing_level += 1
170
171
  der_to_value_without_tracing(der, ber: ber)
171
- RASN1.tracer.tracing_level -= 1
172
+ RASN2.tracer.tracing_level -= 1
172
173
  end
173
174
  end
174
175
 
@@ -191,9 +192,9 @@ module RASN1
191
192
  # @private
192
193
  # der_to_value +der+ with tracing abillity
193
194
  def der_to_value_with_tracing(der, ber: false)
194
- RASN1.tracer.tracing_level += 1
195
+ RASN2.tracer.tracing_level += 1
195
196
  der_to_value_without_tracing(der, ber: ber)
196
- RASN1.tracer.tracing_level -= 1
197
+ RASN2.tracer.tracing_level -= 1
197
198
  end
198
199
  end
199
200
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- module RASN1
3
+ module RASN2
4
4
  module Types
5
5
  # ASN.1 ANY: accepts any types
6
6
  #
@@ -31,67 +31,51 @@ 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
- 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