rasn1 0.10.0 → 0.11.0

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: 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