rasn1 0.10.0 → 0.11.0

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: 0d38c6038ef58e60eb51d02fc20416188918a736386c4273a713f1be2d2535a1
4
- data.tar.gz: fd3b045acdd39707540c3909f24aa8a2e2ae6ed11e46339f90903b7ef96da0df
3
+ metadata.gz: cc6eb3e3649f88dae027153d6136d5e5969135c74a527d4abbc42a42e03e2e73
4
+ data.tar.gz: a674238e2461230d7076ffb29250b3a222c229b7af484b586da25642ba87863a
5
5
  SHA512:
6
- metadata.gz: f21443bccf45e549a1999bedcef1858ff175ab9e65bdb09f048ad4723d4ac64be80338cbff491ddaf31654aabaa1ad567af4925edf24297e757a42c901684ff2
7
- data.tar.gz: ad0a89d6a6d634b83fe89f45f64f327f5740f68be8dfe6ca143bcfcf028e387c9859ec665da82e5f0f8d3576133ef74cf63b24d511da19e2589a53909bcf95ce
6
+ metadata.gz: dba48f620835a6ce4c9b6764d1538a2f96a94f21a7c8aa39b587783ba08b5c745c918eb885276372b3ae215f904596dfb3b5eab818c1a7416131e898c9c220eb
7
+ data.tar.gz: e397c4b1b78320a3b69e24aa9bbf46d29b04653b03aed76f9c906e5e47a03b027957d2d8d6a8f60793f13255b2492f5eb43e9f05a73d3e8dd9bde676cbbcdfe9
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ # Base error class
5
+ class Error < StandardError; end
6
+
7
+ # ASN.1 encoding/decoding error
8
+ class ASN1Error < Error; end
9
+
10
+ # ASN.1 class error
11
+ class ClassError < Error
12
+ # @return [String]
13
+ def message
14
+ "Tag class should be a symbol among: #{Types::Base::CLASSES.keys.join(', ')}"
15
+ end
16
+ end
17
+
18
+ # Enumerated error
19
+ class EnumeratedError < Error; end
20
+
21
+ # CHOICE error: #chosen not set
22
+ class ChoiceError < RASN1::Error
23
+ def message
24
+ "CHOICE #{@name}: #chosen not set"
25
+ end
26
+ end
27
+
28
+ # Exception raised when a constraint is not verified on a constrained type.
29
+ # @version 0.11.0
30
+ # @author Sylvain Daubert
31
+ class ConstraintError < Error
32
+ # @param [Types::Base] object
33
+ def initialize(object)
34
+ @object = object
35
+ super()
36
+ end
37
+
38
+ # @return [String]
39
+ def message
40
+ "Constraint not verified on #{object.inspect}"
41
+ end
42
+ end
43
+ end
data/lib/rasn1/model.rb CHANGED
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative 'types/constrained'
4
+
3
5
  module RASN1
4
6
  # @abstract
5
7
  # {Model} class is a base class to define ASN.1 models.
@@ -18,6 +20,8 @@ module RASN1
18
20
  # integer(:house, explicit: 1, default: 0)])
19
21
  # end
20
22
  #
23
+ # In a model, each element must have a unique name.
24
+ #
21
25
  # === Parse a DER-encoded string
22
26
  # record = Record.parse(der_string)
23
27
  # record[:id] # => RASN1::Types::Integer
@@ -53,16 +57,17 @@ module RASN1
53
57
  #
54
58
  # == Delegation
55
59
  # {Model} may delegate some methods to its root element. Thus, if root element
56
- # is, for example, a {Types::Choice}, model may delegate +#chosen+ and +#chosen_value+.
60
+ # is, for example, a {TypeInts::Choice}, model may delegate +#chosen+ and +#chosen_value+.
57
61
  #
58
62
  # All methods defined by root may be delegated by model, unless model also defines
59
63
  # this method.
60
64
  # @author Sylvain Daubert
61
- class Model # rubocop:disable Metrics/ClassLength
65
+ class Model
62
66
  # @private
63
67
  Elem = Struct.new(:name, :proc_or_class, :content)
64
68
 
65
- class << self
69
+ # @private
70
+ module Accel
66
71
  # @return [Hash]
67
72
  attr_reader :options
68
73
 
@@ -100,120 +105,39 @@ module RASN1
100
105
  klass.class_eval { @root = root }
101
106
  end
102
107
 
103
- # @method sequence(name, options)
104
- # @param [Symbol,String] name name of object in model
105
- # @param [Hash] options
106
- # @return [Elem]
107
- # @see Types::Sequence#initialize
108
- # @method set(name, options)
109
- # @param [Symbol,String] name name of object in model
110
- # @param [Hash] options
111
- # @return [Elem]
112
- # @see Types::Set#initialize
113
- # @method choice(name, options)
114
- # @param [Symbol,String] name name of object in model
115
- # @param [Hash] options
116
- # @return [Elem]
117
- # @see Types::Choice#initialize
118
- %w[sequence set choice].each do |type|
119
- class_eval "def #{type}(name, options={})\n" \
120
- " options.merge!(name: name)\n" \
121
- " proc = proc do |opts|\n" \
122
- " Types::#{type.capitalize}.new(options.merge(opts))\n" \
123
- " end\n" \
124
- " @root = Elem.new(name, proc, options[:content])\n" \
125
- 'end'
108
+ def define_type_accel_base(accel_name, klass)
109
+ singleton_class.class_eval(
110
+ "def #{accel_name}(name, options={})\n" \
111
+ " options[:name] = name\n" \
112
+ " proc = proc do |opts|\n" \
113
+ " #{klass}.new(options.merge(opts))\n" \
114
+ " end\n" \
115
+ " @root = Elem.new(name, proc, options[:content])\n" \
116
+ 'end'
117
+ )
126
118
  end
127
119
 
128
- # @method sequence_of(name, type, options)
129
- # @param [Symbol,String] name name of object in model
130
- # @param [Model, Types::Base] type type for SEQUENCE OF
131
- # @param [Hash] options
132
- # @return [Elem]
133
- # @see Types::SequenceOf#initialize
134
- # @method set_of(name, type, options)
135
- # @param [Symbol,String] name name of object in model
136
- # @param [Model, Types::Base] type type for SET OF
137
- # @param [Hash] options
138
- # @return [Elem]
139
- # @see Types::SetOf#initialize
140
- %w[sequence set].each do |type|
141
- klass_name = "Types::#{type.capitalize}Of"
142
- class_eval "def #{type}_of(name, type, options={})\n" \
143
- " options.merge!(name: name)\n" \
144
- " proc = proc do |opts|\n" \
145
- " #{klass_name}.new(type, options.merge(opts))\n" \
146
- " end\n" \
147
- " @root = Elem.new(name, proc, nil)\n" \
148
- 'end'
120
+ def define_type_accel_of(accel_name, klass)
121
+ singleton_class.class_eval(
122
+ "def #{accel_name}_of(name, type, options={})\n" \
123
+ " options[:name] = name\n" \
124
+ " proc = proc do |opts|\n" \
125
+ " #{klass}.new(type, options.merge(opts))\n" \
126
+ " end\n" \
127
+ " @root = Elem.new(name, proc, nil)\n" \
128
+ 'end'
129
+ )
149
130
  end
150
131
 
151
- # @method boolean(name, options)
152
- # @param [Symbol,String] name name of object in model
153
- # @param [Hash] options
154
- # @return [Elem]
155
- # @see Types::Boolean#initialize
156
- # @method integer(name, options)
157
- # @param [Symbol,String] name name of object in model
158
- # @param [Hash] options
159
- # @return [Elem]
160
- # @see Types::Integer#initialize
161
- # @method bit_string(name, options)
162
- # @param [Symbol,String] name name of object in model
163
- # @param [Hash] options
164
- # @return [Elem]
165
- # @see Types::BitString#initialize
166
- # @method octet_string(name, options)
167
- # @param [Symbol,String] name name of object in model
168
- # @param [Hash] options
169
- # @return [Elem]
170
- # @see Types::OctetString#initialize
171
- # @method null(name, options)
172
- # @param [Symbol,String] name name of object in model
173
- # @param [Hash] options
174
- # @return [Elem]
175
- # @see Types::Null#initialize
176
- # @method enumerated(name, options)
177
- # @param [Symbol,String] name name of object in model
178
- # @param [Hash] options
179
- # @return [Elem]
180
- # @see Types::Enumerated#initialize
181
- # @method utf8_string(name, options)
182
- # @param [Symbol,String] name name of object in model
183
- # @param [Hash] options
184
- # @return [Elem]
185
- # @see Types::Utf8String#initialize
186
- # @method numeric_string(name, options)
187
- # @param [Symbol,String] name name of object in model
188
- # @param [Hash] options
189
- # @return [Elem]
190
- # @see Types::NumericString#initialize
191
- # @method printable_string(name, options)
192
- # @param [Symbol,String] name name of object in model
193
- # @param [Hash] options
194
- # @return [Elem]
195
- # @see Types::PrintableString#initialize
196
- # @method visible_string(name, options)
197
- # @param [Symbol,String] name name of object in model
198
- # @param [Hash] options
199
- # @return [Elem]
200
- # @see Types::VisibleString#initialize
201
- # @method ia5_string(name, options)
202
- # @param [Symbol,String] name name of object in model
203
- # @param [Hash] options
204
- # @return [Elem]
205
- # @see Types::IA5String#initialize
206
- Types.primitives.each do |prim|
207
- next if prim == Types::ObjectId
208
-
209
- method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
210
- class_eval "def #{method_name}(name, options={})\n" \
211
- " options.merge!(name: name)\n" \
212
- " proc = proc do |opts|\n" \
213
- " #{prim}.new(options.merge(opts))\n" \
214
- " end\n" \
215
- " @root = Elem.new(name, proc, nil)\n" \
216
- 'end'
132
+ # Define an accelarator to access a type in a model definition
133
+ # @param [String] accel_name
134
+ # @param [Class] klass
135
+ def define_type_accel(accel_name, klass)
136
+ if klass < Types::SequenceOf
137
+ define_type_accel_of(accel_name, klass)
138
+ else
139
+ define_type_accel_base(accel_name, klass)
140
+ end
217
141
  end
218
142
 
219
143
  # @param [Symbol,String] name name of object in model
@@ -258,6 +182,105 @@ module RASN1
258
182
  end
259
183
  end
260
184
 
185
+ extend Accel
186
+
187
+ # @method sequence(name, options)
188
+ # @param [Symbol,String] name name of object in model
189
+ # @param [Hash] options
190
+ # @return [Elem]
191
+ # @see Types::Sequence#initialize
192
+ # @method set(name, options)
193
+ # @param [Symbol,String] name name of object in model
194
+ # @param [Hash] options
195
+ # @return [Elem]
196
+ # @see Types::Set#initialize
197
+ # @method choice(name, options)
198
+ # @param [Symbol,String] name name of object in model
199
+ # @param [Hash] options
200
+ # @return [Elem]
201
+ # @see Types::Choice#initialize
202
+ %w[sequence set choice].each do |type|
203
+ self.define_type_accel_base(type, Types.const_get(type.capitalize))
204
+ end
205
+
206
+ # @method sequence_of(name, type, options)
207
+ # @param [Symbol,String] name name of object in model
208
+ # @param [Model, Types::Base] type type for SEQUENCE OF
209
+ # @param [Hash] options
210
+ # @return [Elem]
211
+ # @see Types::SequenceOf#initialize
212
+ # @method set_of(name, type, options)
213
+ # @param [Symbol,String] name name of object in model
214
+ # @param [Model, Types::Base] type type for SET OF
215
+ # @param [Hash] options
216
+ # @return [Elem]
217
+ # @see Types::SetOf#initialize
218
+ %w[sequence set].each do |type|
219
+ define_type_accel_of(type, Types.const_get("#{type.capitalize}Of"))
220
+ end
221
+
222
+ # @method boolean(name, options)
223
+ # @param [Symbol,String] name name of object in model
224
+ # @param [Hash] options
225
+ # @return [Elem]
226
+ # @see Types::Boolean#initialize
227
+ # @method integer(name, options)
228
+ # @param [Symbol,String] name name of object in model
229
+ # @param [Hash] options
230
+ # @return [Elem]
231
+ # @see Types::Integer#initialize
232
+ # @method bit_string(name, options)
233
+ # @param [Symbol,String] name name of object in model
234
+ # @param [Hash] options
235
+ # @return [Elem]
236
+ # @see Types::BitString#initialize
237
+ # @method octet_string(name, options)
238
+ # @param [Symbol,String] name name of object in model
239
+ # @param [Hash] options
240
+ # @return [Elem]
241
+ # @see Types::OctetString#initialize
242
+ # @method null(name, options)
243
+ # @param [Symbol,String] name name of object in model
244
+ # @param [Hash] options
245
+ # @return [Elem]
246
+ # @see Types::Null#initialize
247
+ # @method enumerated(name, options)
248
+ # @param [Symbol,String] name name of object in model
249
+ # @param [Hash] options
250
+ # @return [Elem]
251
+ # @see Types::Enumerated#initialize
252
+ # @method utf8_string(name, options)
253
+ # @param [Symbol,String] name name of object in model
254
+ # @param [Hash] options
255
+ # @return [Elem]
256
+ # @see Types::Utf8String#initialize
257
+ # @method numeric_string(name, options)
258
+ # @param [Symbol,String] name name of object in model
259
+ # @param [Hash] options
260
+ # @return [Elem]
261
+ # @see Types::NumericString#initialize
262
+ # @method printable_string(name, options)
263
+ # @param [Symbol,String] name name of object in model
264
+ # @param [Hash] options
265
+ # @return [Elem]
266
+ # @see Types::PrintableString#initialize
267
+ # @method visible_string(name, options)
268
+ # @param [Symbol,String] name name of object in model
269
+ # @param [Hash] options
270
+ # @return [Elem]
271
+ # @see Types::VisibleString#initialize
272
+ # @method ia5_string(name, options)
273
+ # @param [Symbol,String] name name of object in model
274
+ # @param [Hash] options
275
+ # @return [Elem]
276
+ # @see Types::IA5String#initialize
277
+ Types.primitives.each do |prim|
278
+ next if prim == Types::ObjectId
279
+
280
+ method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
281
+ self.define_type_accel_base(method_name, prim)
282
+ end
283
+
261
284
  # Create a new instance of a {Model}
262
285
  # @param [Hash] args
263
286
  def initialize(args={})
@@ -425,7 +448,7 @@ module RASN1
425
448
  when Proc
426
449
  proc_or_class.call(options)
427
450
  when Class
428
- proc_or_class.new
451
+ proc_or_class.new(options)
429
452
  end
430
453
  end
431
454
 
@@ -95,6 +95,13 @@ module RASN1
95
95
  obj
96
96
  end
97
97
 
98
+ # Say if a type is constrained.
99
+ # Always return +false+ for predefined types
100
+ # @return [Booleran]
101
+ def self.constrained?
102
+ false
103
+ end
104
+
98
105
  # @param [Hash] options
99
106
  # @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
100
107
  # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
@@ -110,8 +117,12 @@ module RASN1
110
117
  def initialize(options={})
111
118
  @constructed = nil
112
119
  set_options options
120
+ specific_initializer
113
121
  end
114
122
 
123
+ # @abstract To help subclass initialize itself. Default implementation do nothing.
124
+ def specific_initializer; end
125
+
115
126
  # Used by +#dup+ and +#clone+. Deep copy @value and @default.
116
127
  def initialize_copy(_other)
117
128
  @value = @value.dup
@@ -455,7 +466,7 @@ module RASN1
455
466
  def raise_on_indefinite_length(ber)
456
467
  if primitive?
457
468
  raise ASN1Error, "malformed #{type}: indefinite length " \
458
- 'forbidden for primitive types'
469
+ 'forbidden for primitive types'
459
470
  elsif ber
460
471
  raise NotImplementedError, 'indefinite length not supported'
461
472
  else
@@ -77,15 +77,13 @@ module RASN1
77
77
  def parse!(der, ber: false)
78
78
  parsed = false
79
79
  @value.each_with_index do |element, i|
80
- begin
81
- @chosen = i
82
- nb_bytes = element.parse!(der, ber: ber)
83
- parsed = true
84
- return nb_bytes
85
- rescue ASN1Error
86
- @chosen = nil
87
- next
88
- end
80
+ @chosen = i
81
+ nb_bytes = element.parse!(der, ber: ber)
82
+ parsed = true
83
+ return nb_bytes
84
+ rescue ASN1Error
85
+ @chosen = nil
86
+ next
89
87
  end
90
88
  raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
91
89
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # Mixin to had constraints on a RASN1 type.
6
+ # Should not be used directly but through {Model.define_type}.
7
+ # @version 0.11.0
8
+ # @author Sylvain Daubert
9
+ module Constrained
10
+ module ClassMethods
11
+ # Setter for constraint
12
+ # @param [Proc,nil] constraint
13
+ # @return [Proc,nil]
14
+ def constraint=(constraint)
15
+ @constraint = constraint
16
+ end
17
+
18
+ # Check if a constraint is really defined
19
+ # @return [Boolean]
20
+ def constrained?
21
+ @constraint.is_a?(Proc)
22
+ end
23
+
24
+ # Check constraint, if defined
25
+ # @param [Object] value the value of the type to check
26
+ # @raise [ConstraintError] constraint is not verified
27
+ def check_constraint(value)
28
+ return unless constrained?
29
+ raise ConstraintError.new(self) unless @constraint.call(value)
30
+ end
31
+ end
32
+
33
+ class << self
34
+ attr_reader :constraint
35
+
36
+ def included(base)
37
+ base.extend ClassMethods
38
+ end
39
+ end
40
+
41
+ # Redefined +#value=+ to check constraint before assigning +val+
42
+ # @see Types::Base#value=
43
+ # @raise [ConstraintError] constraint is not verified
44
+ def value=(val)
45
+ self.class.check_constraint(val)
46
+ super
47
+ end
48
+
49
+ def der_to_value(der, ber: false)
50
+ super
51
+ self.class.check_constraint(@value)
52
+ end
53
+ end
54
+ end
55
+ end
data/lib/rasn1/types.rb CHANGED
@@ -73,6 +73,29 @@ module RASN1
73
73
  @id2types.default = Types::Base
74
74
  @id2types.freeze
75
75
  end
76
+
77
+ # Define a new ASN.1 type from a base one.
78
+ # This new type may have a constraint defines on it.
79
+ # @param [Symbol,String] name New type name. Must start with a capital letter.
80
+ # @param [Types::Base] from
81
+ # @return [Class] newly created class
82
+ def self.define_type(name, from:, &block)
83
+ constraint = block.nil? ? nil : block.to_proc
84
+
85
+ new_klass = Class.new(from) do
86
+ include Constrained
87
+ end
88
+ new_klass.constraint = constraint
89
+
90
+ self.const_set(name, new_klass)
91
+ Model.define_type_accel(name.downcase, new_klass)
92
+
93
+ # Empty type caches
94
+ @primitives = nil
95
+ @constructed = nil
96
+
97
+ new_klass
98
+ end
76
99
  end
77
100
  end
78
101
 
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.10.0'
4
+ VERSION = '0.11.0'
5
5
  end
data/lib/rasn1.rb CHANGED
@@ -1,36 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'rasn1/version'
4
+ require 'rasn1/errors'
4
5
  require 'rasn1/types'
5
6
  require 'rasn1/model'
6
7
 
7
8
  # Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
8
9
  # @author Sylvain Daubert
9
10
  module RASN1
10
- # Base error class
11
- class Error < StandardError; end
12
-
13
- # ASN.1 encoding/decoding error
14
- class ASN1Error < Error; end
15
-
16
- # ASN.1 class error
17
- class ClassError < Error
18
- # @return [String]
19
- def message
20
- "Tag class should be a symbol among: #{Types::Base::CLASSES.keys.join(', ')}"
21
- end
22
- end
23
-
24
- # Enumerated error
25
- class EnumeratedError < Error; end
26
-
27
- # CHOICE error: #chosen not set
28
- class ChoiceError < RASN1::Error
29
- def message
30
- "CHOICE #{@name}: #chosen not set"
31
- end
32
- end
33
-
34
11
  # Parse a DER/BER string without checking a model
35
12
  # @note If you want to check ASN.1 grammary, you should define a {Model}
36
13
  # and use {Model#parse}.
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.10.0
4
+ version: 0.11.0
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-03-11 00:00:00.000000000 Z
11
+ date: 2022-10-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -66,6 +66,7 @@ files:
66
66
  - LICENSE
67
67
  - README.md
68
68
  - lib/rasn1.rb
69
+ - lib/rasn1/errors.rb
69
70
  - lib/rasn1/model.rb
70
71
  - lib/rasn1/types.rb
71
72
  - lib/rasn1/types/any.rb
@@ -73,6 +74,7 @@ files:
73
74
  - lib/rasn1/types/bit_string.rb
74
75
  - lib/rasn1/types/boolean.rb
75
76
  - lib/rasn1/types/choice.rb
77
+ - lib/rasn1/types/constrained.rb
76
78
  - lib/rasn1/types/constructed.rb
77
79
  - lib/rasn1/types/enumerated.rb
78
80
  - lib/rasn1/types/generalized_time.rb