rasn1 0.11.0 → 0.12.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.
@@ -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,17 +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
- @value = @enum.key(@value)
137
- raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless value?
141
+ @value = int_to_enum(@value)
138
142
  end
139
143
 
140
144
  def explicit_type
141
- 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*')})"
142
154
  end
143
155
  end
144
156
  end
@@ -15,6 +15,12 @@ module RASN1
15
15
  str
16
16
  end
17
17
 
18
+ # @return [Boolean]
19
+ # Build only if not optional
20
+ def can_build?
21
+ !optional?
22
+ end
23
+
18
24
  private
19
25
 
20
26
  def value_to_der
@@ -18,7 +18,7 @@ module RASN1
18
18
 
19
19
  def value_to_der
20
20
  check_characters
21
- @value.to_s.force_encoding('BINARY')
21
+ @value.to_s.b
22
22
  end
23
23
 
24
24
  def der_to_value(der, ber: false)
@@ -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}"
@@ -18,7 +18,7 @@ module RASN1
18
18
 
19
19
  def value_to_der
20
20
  check_characters
21
- @value.to_s.force_encoding('BINARY')
21
+ @value.to_s.b
22
22
  end
23
23
 
24
24
  def der_to_value(der, ber: false)
@@ -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
@@ -59,7 +59,6 @@ module RASN1
59
59
  Sequence.encoded_type
60
60
  end
61
61
 
62
- # @param [Symbol, String] name name for this tag in grammar
63
62
  # @param [Class, Base] of_type base type for sequence of
64
63
  # @see Base#initialize
65
64
  def initialize(of_type, options={})
@@ -68,7 +67,8 @@ module RASN1
68
67
  @no_value = false
69
68
  end
70
69
 
71
- def initialize_copy(other)
70
+ # Clone @#of_type and values
71
+ def initialize_copy(*)
72
72
  super
73
73
  @of_type = @of_type.dup
74
74
  @value = @value.map(&:dup)
@@ -105,6 +105,8 @@ module RASN1
105
105
  @value.length
106
106
  end
107
107
 
108
+ # @param [::Integer] level
109
+ # @return [String]
108
110
  def inspect(level=0)
109
111
  str = common_inspect(level)
110
112
  str << "\n"
@@ -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
@@ -35,20 +35,22 @@ module RASN1
35
35
 
36
36
  def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
37
37
  format = case der.size
38
- when 11
39
- '%Y%m%d%H%MZ'
40
- when 13
41
- '%Y%m%d%H%M%SZ'
42
- when 15
38
+ when 11, 15
43
39
  '%Y%m%d%H%M%z'
44
- when 17
40
+ when 13, 17
45
41
  '%Y%m%d%H%M%S%z'
46
42
  else
47
43
  prefix = @name.nil? ? type : "tag #{@name}"
48
44
  raise ASN1Error, "#{prefix}: unrecognized format: #{der}"
49
45
  end
50
46
  century = (Time.now.year / 100).to_s
51
- @value = DateTime.strptime(century + der, format).to_time
47
+ @value = Strptime.new(format).exec(century + der)
48
+ end
49
+
50
+ def trace_data
51
+ return super if explicit?
52
+
53
+ +' ' << raw_data
52
54
  end
53
55
  end
54
56
  end
@@ -17,12 +17,12 @@ module RASN1
17
17
  private
18
18
 
19
19
  def value_to_der
20
- @value.to_s.force_encoding('UTF-8').force_encoding('BINARY')
20
+ @value.to_s.dup.force_encoding('UTF-8').b
21
21
  end
22
22
 
23
23
  def der_to_value(der, ber: false)
24
24
  super
25
- @value = der.force_encoding('UTF-8')
25
+ @value = der.dup.force_encoding('UTF-8')
26
26
  end
27
27
  end
28
28
  end
data/lib/rasn1/types.rb CHANGED
@@ -4,18 +4,25 @@ module RASN1
4
4
  # This modules is a namesapce for all ASN.1 type classes.
5
5
  # @author Sylvain Daubert
6
6
  module Types
7
+ @primitives = []
8
+ @constructed = []
9
+
7
10
  # Give all primitive types
8
11
  # @return [Array<Types::Primitive>]
9
12
  def self.primitives
10
- @primitives ||= self.constants.map { |c| Types.const_get(c) }
11
- .select { |klass| klass < Primitive }
13
+ return @primitives unless @primitives.empty?
14
+
15
+ @primitives = self.constants.map { |c| Types.const_get(c) }
16
+ .select { |klass| klass < Primitive }
12
17
  end
13
18
 
14
19
  # Give all constructed types
15
20
  # @return [Array<Types::Constructed>]
16
21
  def self.constructed
17
- @constructed ||= self.constants.map { |c| Types.const_get(c) }
18
- .select { |klass| klass < Constructed }
22
+ return @constructed unless @constructed.empty?
23
+
24
+ @constructed = self.constants.map { |c| Types.const_get(c) }
25
+ .select { |klass| klass < Constructed }
19
26
  end
20
27
 
21
28
  # @private
@@ -67,8 +74,8 @@ module RASN1
67
74
  def self.generate_id2type_cache
68
75
  constructed = self.constructed - [Types::SequenceOf, Types::SetOf]
69
76
  primitives = self.primitives - [Types::Enumerated]
70
- ary = (primitives + constructed).select { |type| type.const_defined? :ID }
71
- .map { |type| [type::ID, type] }
77
+ ary = (primitives + constructed).select { |type| type.const_defined?(:ID) }
78
+ .map { |type| [type.const_get(:ID), type] }
72
79
  @id2types = ary.to_h
73
80
  @id2types.default = Types::Base
74
81
  @id2types.freeze
@@ -77,9 +84,20 @@ module RASN1
77
84
  # Define a new ASN.1 type from a base one.
78
85
  # This new type may have a constraint defines on it.
79
86
  # @param [Symbol,String] name New type name. Must start with a capital letter.
80
- # @param [Types::Base] from
87
+ # @param [Types::Base] from class from which inherits
88
+ # @param [Module] in_module module in which creates new type (default to {RASN1::Types})
81
89
  # @return [Class] newly created class
82
- def self.define_type(name, from:, &block)
90
+ # @yieldparam [Object] value value to set to type, or infered at parsing
91
+ # @yieldreturn [Boolean]
92
+ # @since 0.11.0
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
100
+ def self.define_type(name, from:, in_module: self, &block)
83
101
  constraint = block.nil? ? nil : block.to_proc
84
102
 
85
103
  new_klass = Class.new(from) do
@@ -87,12 +105,13 @@ module RASN1
87
105
  end
88
106
  new_klass.constraint = constraint
89
107
 
90
- self.const_set(name, new_klass)
91
- Model.define_type_accel(name.downcase, new_klass)
108
+ in_module.const_set(name, new_klass)
109
+ accel_name = name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
110
+ Model.define_type_accel(accel_name, new_klass)
92
111
 
93
112
  # Empty type caches
94
- @primitives = nil
95
- @constructed = nil
113
+ @primitives = []
114
+ @constructed = []
96
115
 
97
116
  new_klass
98
117
  end
@@ -108,6 +127,7 @@ require_relative 'types/octet_string'
108
127
  require_relative 'types/null'
109
128
  require_relative 'types/object_id'
110
129
  require_relative 'types/enumerated'
130
+ require_relative 'types/bmp_string'
111
131
  require_relative 'types/utf8_string'
112
132
  require_relative 'types/numeric_string'
113
133
  require_relative 'types/printable_string'
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.11.0'
4
+ VERSION = '0.12.1'
5
5
  end
@@ -0,0 +1,209 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'delegate'
4
+
5
+ module RASN1
6
+ # This class is used to wrap a {Types::Base} or {Model} instance to force its options.
7
+ #
8
+ # == Usage
9
+ # This class may be used to wrap another RASN1 object by 3 ways:
10
+ # * wrap an object to modify its options,
11
+ # * implicitly wrap an object (i.e. change its tag),
12
+ # * explicitly wrap an object (i.e wrap the object in another explicit ASN.1 tag)
13
+ #
14
+ # @example
15
+ # # object to wrap
16
+ # int = RASN1::Types::Integer.new(implicit: 1) # its tag is 0x81
17
+ # # simple wrapper, change an option
18
+ # wrapper = RASN1::Wrapper.new(int, default: 1)
19
+ # # implicit wrapper
20
+ # wrapper = RASN1::Wrapper.new(int, implicit: 3) # wrapped int tag is now 0x83
21
+ # # explicit wrapper
22
+ # wrapper = RASN1::Wrapper.new(int, explicit: 4) # int tag is always 0x81, but it is wrapped in a 0x84 tag
23
+ # @since 0.12.0
24
+ class Wrapper < SimpleDelegator
25
+ # @private Private class used to build/parse explicit wrappers
26
+ class ExplicitWrapper < Types::Base
27
+ ID = 0 # not used
28
+ ASN1_PC = 0 # not constructed
29
+
30
+ def self.type
31
+ ''
32
+ end
33
+
34
+ # @return [Boolean]
35
+ # @see Types::Base#can_build?
36
+ def can_build?
37
+ ok = super
38
+ return ok unless optional?
39
+
40
+ ok && @value.can_build?
41
+ end
42
+
43
+ private
44
+
45
+ def value_to_der
46
+ @value.is_a?(String) ? @value : @value.to_der
47
+ end
48
+
49
+ def inspect_value
50
+ ''
51
+ end
52
+ end
53
+
54
+ # @param [Types::Base,Model] element element to wrap
55
+ # @param [Hash] options
56
+ def initialize(element, options={})
57
+ opts = explicit_implicit(options)
58
+
59
+ if explicit?
60
+ generate_explicit_wrapper(opts)
61
+ element.options = element.options.merge(generate_explicit_wrapper_options(opts))
62
+ @options = opts
63
+ else
64
+ opts[:value] = element.value
65
+ element.options = element.options.merge(opts)
66
+ @options = {}
67
+ end
68
+ raise RASN1::Error, 'Cannot be implicit and explicit' if explicit? && implicit?
69
+
70
+ super(element)
71
+ end
72
+
73
+ # Say if wrapper is an explicit one (i.e. add tag and length to its element)
74
+ # @return [Boolean]
75
+ def explicit?
76
+ !!@explicit
77
+ end
78
+
79
+ # Say if wrapper is an implicit one (i.e. change tag of its element)
80
+ # @return [Boolean]
81
+ def implicit?
82
+ !!@implicit
83
+ end
84
+
85
+ # Convert wrapper and its element to a DER string
86
+ # @return [String]
87
+ def to_der
88
+ if implicit?
89
+ el = generate_implicit_element
90
+ el.to_der
91
+ elsif explicit?
92
+ @explicit_wrapper.value = element
93
+ @explicit_wrapper.to_der
94
+ else
95
+ element.to_der
96
+ end
97
+ end
98
+
99
+ # Parse a DER string. This method updates object.
100
+ # @param [String] der DER string
101
+ # @param [Boolean] ber if +true+, accept BER encoding
102
+ # @return [Integer] total number of parsed bytes
103
+ # @raise [ASN1Error] error on parsing
104
+ def parse!(der, ber: false)
105
+ if implicit?
106
+ el = generate_implicit_element
107
+ parsed = el.parse!(der, ber: ber)
108
+ element.value = el.value
109
+ parsed
110
+ elsif explicit?
111
+ parsed = @explicit_wrapper.parse!(der, ber: ber)
112
+ element.parse!(@explicit_wrapper.value, ber: ber) if parsed.positive?
113
+ parsed
114
+ else
115
+ element.parse!(der, ber: ber)
116
+ end
117
+ end
118
+
119
+ # @return [Boolean]
120
+ # @see Types::Base#value?
121
+ def value?
122
+ if explicit?
123
+ @explicit_wrapper.value?
124
+ else
125
+ __getobj__.value?
126
+ end
127
+ end
128
+
129
+ # Return Wrapped element
130
+ # @return [Types::Base,Model]
131
+ def element
132
+ __getobj__
133
+ end
134
+
135
+ # @return [::Integer]
136
+ # @see Types::Base#id
137
+ def id
138
+ if implicit?
139
+ @implicit
140
+ elsif explicit?
141
+ @explicit
142
+ else
143
+ element.id
144
+ end
145
+ end
146
+
147
+ # @return [Symbol]
148
+ # @see Types::Base#asn1_class
149
+ def asn1_class
150
+ return element.asn1_class unless @options.key?(:class)
151
+
152
+ @options[:class]
153
+ end
154
+
155
+ # @return [Boolean]
156
+ # @see Types::Base#constructed
157
+ def constructed?
158
+ return element.constructed? unless @options.key?(:constructed)
159
+
160
+ @options[:constructed]
161
+ end
162
+
163
+ # @return [Boolean]
164
+ # @see Types::Base#primitive
165
+ def primitive?
166
+ !constructed?
167
+ end
168
+
169
+ # @param [::Integer] level
170
+ # @return [String]
171
+ def inspect(level=0)
172
+ return super(level) unless explicit?
173
+
174
+ @explicit_wrapper.inspect(level) << ' ' << super(level)
175
+ end
176
+
177
+ private
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
+
199
+ def generate_implicit_element
200
+ el = element.dup
201
+ if el.explicit?
202
+ el.options = el.options.merge(explicit: @implicit)
203
+ elsif el.implicit?
204
+ el.options = el.options.merge(implicit: @implicit)
205
+ end
206
+ el
207
+ end
208
+ end
209
+ end
data/lib/rasn1.rb CHANGED
@@ -1,13 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'rasn1/version'
4
- require 'rasn1/errors'
5
- require 'rasn1/types'
6
- require 'rasn1/model'
3
+ require_relative 'rasn1/version'
4
+ require_relative 'rasn1/errors'
5
+ require_relative 'rasn1/types'
6
+ require_relative 'rasn1/model'
7
+ require_relative 'rasn1/wrapper'
8
+ require_relative 'rasn1/tracer'
7
9
 
8
10
  # Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
9
11
  # @author Sylvain Daubert
10
12
  module RASN1
13
+ # @private
14
+ CONTAINER_CLASSES = [Types::Sequence, Types::Set].freeze
15
+
11
16
  # Parse a DER/BER string without checking a model
12
17
  # @note If you want to check ASN.1 grammary, you should define a {Model}
13
18
  # and use {Model#parse}.
@@ -18,24 +23,21 @@ module RASN1
18
23
  # @param [String] der binary string to parse
19
24
  # @param [Boolean] ber if +true+, decode a BER string, else a DER one
20
25
  # @return [Types::Base]
21
- def self.parse(der, ber: false)
22
- root = nil
23
- until der.empty?
24
- type = Types.id2type(der)
25
- type.parse!(der, ber: ber)
26
- root ||= type
26
+ def self.parse(der, ber: false) # rubocop:disable Metrics:AbcSize
27
+ type = Types.id2type(der)
28
+ type.parse!(der, ber: ber)
27
29
 
28
- if [Types::Sequence, Types::Set].include? type.class
29
- subder = type.value
30
- ary = []
31
- until subder.empty?
32
- ary << self.parse(subder)
33
- subder = subder[ary.last.to_der.size..-1]
34
- end
35
- 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]
36
37
  end
37
- der = der[type.to_der.size..-1]
38
+ RASN1.tracer.tracing_level -= 1 unless RASN1.tracer.nil?
39
+ type.value = ary
38
40
  end
39
- root
41
+ type
40
42
  end
41
43
  end
metadata CHANGED
@@ -1,57 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rasn1
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.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-10-13 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
- name: rake
14
+ name: strptime
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '12.3'
20
- type: :development
19
+ version: 0.2.5
20
+ type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '12.3'
27
- - !ruby/object:Gem::Dependency
28
- name: rspec
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: '3.0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: '3.0'
41
- - !ruby/object:Gem::Dependency
42
- name: yard
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - "~>"
46
- - !ruby/object:Gem::Version
47
- version: '0.9'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - "~>"
53
- - !ruby/object:Gem::Version
54
- version: '0.9'
26
+ version: 0.2.5
55
27
  description: |
56
28
  RASN1 is a pure ruby ASN.1 library. It may encode and decode DER and BER
57
29
  encodings.
@@ -68,10 +40,12 @@ files:
68
40
  - lib/rasn1.rb
69
41
  - lib/rasn1/errors.rb
70
42
  - lib/rasn1/model.rb
43
+ - lib/rasn1/tracer.rb
71
44
  - lib/rasn1/types.rb
72
45
  - lib/rasn1/types/any.rb
73
46
  - lib/rasn1/types/base.rb
74
47
  - lib/rasn1/types/bit_string.rb
48
+ - lib/rasn1/types/bmp_string.rb
75
49
  - lib/rasn1/types/boolean.rb
76
50
  - lib/rasn1/types/choice.rb
77
51
  - lib/rasn1/types/constrained.rb
@@ -94,6 +68,7 @@ files:
94
68
  - lib/rasn1/types/utf8_string.rb
95
69
  - lib/rasn1/types/visible_string.rb
96
70
  - lib/rasn1/version.rb
71
+ - lib/rasn1/wrapper.rb
97
72
  homepage: https://github.com/sdaubert/rasn1
98
73
  licenses:
99
74
  - MIT