rasn1 0.6.8 → 0.9.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: 1889a0ada74e2d9255ce6af799caa441fd2d2cacfaf0097b5afd1730ed9a9fd9
4
- data.tar.gz: e0453969129814385bd272c362373b12ae0e38201145280c360ac3696dd95cb1
3
+ metadata.gz: b0f5f3f69aaae20cfd16576f565bc52d198bea49921a0db15e3e2a95a4947853
4
+ data.tar.gz: c3c1f6b2d5019febcbff3812611d624094b7aaefc238bd70109d4c278be3af8c
5
5
  SHA512:
6
- metadata.gz: 4dc47229a202f0bfa7d56a129cd5e1807009b9147da6e844d5a47589fbf0e8eab9bebbb51f81866a0222eb4a6dbf953d381f266ec332e697b6caf1b894fa6c18
7
- data.tar.gz: 4fc5127882d478d111452dff0cac6e226d270f47b4798b6a365f735c0bb1402ec417640b48be9839f7336fcf6d526f2a771d90bdc6ed64b0cefc3258d9e22bff
6
+ metadata.gz: 51e54a40d6608074e76ba0d18d51462e82fba109a4258b9082875bafe36476060186d2081cabb4e2d17e1140f53a4700906a053fdb05638ceb060560a337d05b
7
+ data.tar.gz: 434ca0398606fb411351a2d43b6701b70f9512043abea8768e21832a2a1800be8347efef50b17b01239ce953b49cb21b64860e2d252cc64e9c696a514de564ea
data/README.md CHANGED
@@ -1,5 +1,4 @@
1
1
  [![Gem Version](https://badge.fury.io/rb/rasn1.svg)](https://badge.fury.io/rb/rasn1)
2
- [![Build Status](https://travis-ci.org/sdaubert/rasn1.svg?branch=master)](https://travis-ci.org/sdaubert/rasn1)
3
2
 
4
3
  # Rasn1
5
4
 
@@ -15,13 +14,22 @@ gem 'rasn1'
15
14
 
16
15
  And then execute:
17
16
 
18
- $ bundle
17
+ $ bundle install
19
18
 
20
19
  Or install it yourself as:
21
20
 
22
21
  $ gem install rasn1
23
22
 
24
- ## Usage
23
+ ## Simple usage
24
+
25
+ To decode a DER/BER string without checking a model, do:
26
+
27
+ ```ruby
28
+ decoded_der = RASN1.parse(der_string)
29
+ decoded_ber = RASN1.parse(ber_string, ber: true)
30
+ ```
31
+
32
+ ## Advanced usage
25
33
  All examples below will be based on:
26
34
 
27
35
  ```
data/lib/rasn1/model.rb CHANGED
@@ -1,5 +1,6 @@
1
- module RASN1
1
+ # frozen_string_literal: true
2
2
 
3
+ module RASN1
3
4
  # @abstract
4
5
  # {Model} class is a base class to define ASN.1 models.
5
6
  # == Create a simple ASN.1 model
@@ -57,7 +58,9 @@ module RASN1
57
58
  # All methods defined by root may be delegated by model, unless model also defines
58
59
  # this method.
59
60
  # @author Sylvain Daubert
60
- class Model
61
+ class Model # rubocop:disable Metrics/ClassLength
62
+ # @private
63
+ Elem = Struct.new(:name, :proc_or_class, :content)
61
64
 
62
65
  class << self
63
66
  # @return [Hash]
@@ -66,8 +69,9 @@ module RASN1
66
69
  # Use another model in this model
67
70
  # @param [String,Symbol] name
68
71
  # @param [Class] model_klass
72
+ # @return [Elem]
69
73
  def model(name, model_klass)
70
- @root = [name, model_klass]
74
+ @root = Elem.new(name, model_klass, nil)
71
75
  end
72
76
 
73
77
  # Update options of root element.
@@ -92,135 +96,153 @@ module RASN1
92
96
  # @return [void]
93
97
  def inherited(klass)
94
98
  super
95
- root = defined?(@root )? @root : nil
99
+ root = @root
96
100
  klass.class_eval { @root = root }
97
101
  end
98
102
 
99
103
  # @method sequence(name, options)
100
104
  # @param [Symbol,String] name name of object in model
101
105
  # @param [Hash] options
106
+ # @return [Elem]
102
107
  # @see Types::Sequence#initialize
103
108
  # @method set(name, options)
104
109
  # @param [Symbol,String] name name of object in model
105
110
  # @param [Hash] options
111
+ # @return [Elem]
106
112
  # @see Types::Set#initialize
107
113
  # @method choice(name, options)
108
114
  # @param [Symbol,String] name name of object in model
109
115
  # @param [Hash] options
116
+ # @return [Elem]
110
117
  # @see Types::Choice#initialize
111
- %w(sequence set choice).each do |type|
118
+ %w[sequence set choice].each do |type|
112
119
  class_eval "def #{type}(name, options={})\n" \
113
120
  " options.merge!(name: name)\n" \
114
- " proc = Proc.new do |opts|\n" \
121
+ " proc = proc do |opts|\n" \
115
122
  " Types::#{type.capitalize}.new(options.merge(opts))\n" \
116
123
  " end\n" \
117
- " @root = [name, proc]\n" \
118
- " @root << options[:content] unless options[:content].nil?\n" \
119
- " @root\n" \
120
- "end"
124
+ " @root = Elem.new(name, proc, options[:content])\n" \
125
+ 'end'
121
126
  end
122
127
 
123
128
  # @method sequence_of(name, type, options)
124
129
  # @param [Symbol,String] name name of object in model
125
130
  # @param [Model, Types::Base] type type for SEQUENCE OF
126
131
  # @param [Hash] options
132
+ # @return [Elem]
127
133
  # @see Types::SequenceOf#initialize
128
134
  # @method set_of(name, type, options)
129
135
  # @param [Symbol,String] name name of object in model
130
136
  # @param [Model, Types::Base] type type for SET OF
131
137
  # @param [Hash] options
138
+ # @return [Elem]
132
139
  # @see Types::SetOf#initialize
133
- %w(sequence set).each do |type|
140
+ %w[sequence set].each do |type|
134
141
  klass_name = "Types::#{type.capitalize}Of"
135
142
  class_eval "def #{type}_of(name, type, options={})\n" \
136
143
  " options.merge!(name: name)\n" \
137
- " proc = Proc.new do |opts|\n" \
144
+ " proc = proc do |opts|\n" \
138
145
  " #{klass_name}.new(type, options.merge(opts))\n" \
139
146
  " end\n" \
140
- " @root = [name, proc]\n" \
141
- "end"
147
+ " @root = Elem.new(name, proc, nil)\n" \
148
+ 'end'
142
149
  end
143
150
 
144
151
  # @method boolean(name, options)
145
152
  # @param [Symbol,String] name name of object in model
146
153
  # @param [Hash] options
154
+ # @return [Elem]
147
155
  # @see Types::Boolean#initialize
148
156
  # @method integer(name, options)
149
157
  # @param [Symbol,String] name name of object in model
150
158
  # @param [Hash] options
159
+ # @return [Elem]
151
160
  # @see Types::Integer#initialize
152
161
  # @method bit_string(name, options)
153
162
  # @param [Symbol,String] name name of object in model
154
163
  # @param [Hash] options
164
+ # @return [Elem]
155
165
  # @see Types::BitString#initialize
156
166
  # @method octet_string(name, options)
157
167
  # @param [Symbol,String] name name of object in model
158
168
  # @param [Hash] options
169
+ # @return [Elem]
159
170
  # @see Types::OctetString#initialize
160
171
  # @method null(name, options)
161
172
  # @param [Symbol,String] name name of object in model
162
173
  # @param [Hash] options
174
+ # @return [Elem]
163
175
  # @see Types::Null#initialize
164
176
  # @method enumerated(name, options)
165
177
  # @param [Symbol,String] name name of object in model
166
178
  # @param [Hash] options
179
+ # @return [Elem]
167
180
  # @see Types::Enumerated#initialize
168
181
  # @method utf8_string(name, options)
169
182
  # @param [Symbol,String] name name of object in model
170
183
  # @param [Hash] options
184
+ # @return [Elem]
171
185
  # @see Types::Utf8String#initialize
172
186
  # @method numeric_string(name, options)
173
187
  # @param [Symbol,String] name name of object in model
174
188
  # @param [Hash] options
189
+ # @return [Elem]
175
190
  # @see Types::NumericString#initialize
176
191
  # @method printable_string(name, options)
177
192
  # @param [Symbol,String] name name of object in model
178
193
  # @param [Hash] options
194
+ # @return [Elem]
179
195
  # @see Types::PrintableString#initialize
180
196
  # @method visible_string(name, options)
181
197
  # @param [Symbol,String] name name of object in model
182
198
  # @param [Hash] options
199
+ # @return [Elem]
183
200
  # @see Types::VisibleString#initialize
184
201
  # @method ia5_string(name, options)
185
202
  # @param [Symbol,String] name name of object in model
186
203
  # @param [Hash] options
204
+ # @return [Elem]
187
205
  # @see Types::IA5String#initialize
188
206
  Types.primitives.each do |prim|
189
207
  next if prim == Types::ObjectId
208
+
190
209
  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
191
210
  class_eval "def #{method_name}(name, options={})\n" \
192
211
  " options.merge!(name: name)\n" \
193
- " proc = Proc.new do |opts|\n" \
194
- " #{prim.to_s}.new(options.merge(opts))\n" \
212
+ " proc = proc do |opts|\n" \
213
+ " #{prim}.new(options.merge(opts))\n" \
195
214
  " end\n" \
196
- " @root = [name, proc]\n" \
197
- "end"
215
+ " @root = Elem.new(name, proc, nil)\n" \
216
+ 'end'
198
217
  end
199
218
 
200
219
  # @param [Symbol,String] name name of object in model
201
220
  # @param [Hash] options
221
+ # @return [Elem]
202
222
  # @note This method is named +objectid+ and not +object_id+ to not override
203
223
  # +Object#object_id+.
204
224
  # @see Types::ObjectId#initialize
205
225
  def objectid(name, options={})
206
- options.merge!(name: name)
207
- proc = Proc.new { |opts| Types::ObjectId.new(options.merge(opts)) }
208
- @root = [name, proc]
226
+ options[:name] = name
227
+ proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) }
228
+ @root = Elem.new(name, proc, nil)
209
229
  end
210
230
 
211
231
  # @param [Symbol,String] name name of object in model
212
232
  # @param [Hash] options
233
+ # @return [Elem]
213
234
  # @see Types::Any#initialize
214
235
  def any(name, options={})
215
- options.merge!(name: name)
216
- proc = Proc.new { |opts| Types::Any.new(options.merge(opts)) }
217
- @root = [name, proc]
236
+ options[:name] = name
237
+ proc = proc { |opts| Types::Any.new(options.merge(opts)) }
238
+ @root = Elem.new(name, proc, nil)
218
239
  end
219
240
 
220
241
  # Give type name (aka class name)
221
242
  # @return [String]
222
243
  def type
223
244
  return @type if defined? @type
245
+
224
246
  @type = self.to_s.gsub(/.*::/, '')
225
247
  end
226
248
 
@@ -231,7 +253,7 @@ module RASN1
231
253
  # @raise [ASN1Error] error on parsing
232
254
  def parse(str, ber: false)
233
255
  model = new
234
- model.parse! str, ber: ber
256
+ model.parse!(str, ber: ber)
235
257
  model
236
258
  end
237
259
  end
@@ -240,8 +262,8 @@ module RASN1
240
262
  # @param [Hash] args
241
263
  def initialize(args={})
242
264
  root = generate_root
243
- set_elements(*root)
244
- initialize_elements self, args
265
+ set_elements(root)
266
+ initialize_elements(self, args)
245
267
  end
246
268
 
247
269
  # Give access to element +name+ in model
@@ -256,14 +278,15 @@ module RASN1
256
278
  # @param [Object] value
257
279
  # @return [Object] value
258
280
  def []=(name, value)
259
- raise Error, "cannot set value for a Model" if @elements[name].is_a? Model
281
+ raise Error, 'cannot set value for a Model' if @elements[name].is_a? Model
282
+
260
283
  @elements[name].value = value
261
284
  end
262
285
 
263
- # Get name frm root type
286
+ # Get name from root type
264
287
  # @return [String,Symbol]
265
288
  def name
266
- @root
289
+ @root_name
267
290
  end
268
291
 
269
292
  # Get elements names
@@ -278,15 +301,15 @@ module RASN1
278
301
  private_to_h
279
302
  end
280
303
 
281
- # @return [String]
282
- def to_der
283
- @elements[@root].to_der
284
- end
285
-
286
304
  # Get root element from model
287
305
  # @return [Types::Base,Model]
288
306
  def root
289
- @elements[@root]
307
+ @elements[@root_name]
308
+ end
309
+
310
+ # @return [String]
311
+ def to_der
312
+ root.to_der
290
313
  end
291
314
 
292
315
  # Give type name (aka class name)
@@ -301,19 +324,62 @@ module RASN1
301
324
  # @return [Integer] number of parsed bytes
302
325
  # @raise [ASN1Error] error on parsing
303
326
  def parse!(str, ber: false)
304
- @elements[@root].parse!(str.dup.force_encoding('BINARY'), ber: ber)
327
+ root.parse!(str.dup.force_encoding('BINARY'), ber: ber)
328
+ end
329
+
330
+ # @overload value
331
+ # Get value of root element
332
+ # @return [Object,nil]
333
+ # @overload value(name, *args)
334
+ # Direct access to the value of +name+ (nested) element of model.
335
+ # @param [String,Symbol] name
336
+ # @param [Array<Integer,String,Symbol>] args more argument to access element. May be
337
+ # used to access content of a SequenceOf or a SetOf
338
+ # @return [Object,nil]
339
+ # @return [Object,nil]
340
+ # @example
341
+ # class MyModel1 < RASN1::Model
342
+ # sequence('seq', content: [boolean('boolean'), integer('int')])
343
+ # end
344
+ # class MyModel2 < RASN1::Model
345
+ # sequence('root', content: [sequence_of('list', MyModel1)])
346
+ # end
347
+ # model = MyModel2.new
348
+ # model.parse!(der)
349
+ # # access to 2nd MyModel1.int in list
350
+ # model.value('list', 1, 'int')
351
+ def value(name=nil, *args)
352
+ if name.nil?
353
+ root.value
354
+ else
355
+ elt = by_name(name)
356
+
357
+ unless args.empty?
358
+ args.each do |arg|
359
+ elt = elt.root if elt.is_a?(Model)
360
+ elt = elt[arg]
361
+ end
362
+ end
363
+
364
+ elt.value
365
+ end
305
366
  end
306
367
 
307
368
  # Delegate some methods to root element
308
369
  # @param [Symbol] meth
309
370
  def method_missing(meth, *args)
310
- if @elements[@root].respond_to? meth
311
- @elements[@root].send meth, *args
371
+ if root.respond_to? meth
372
+ root.send meth, *args
312
373
  else
313
374
  super
314
375
  end
315
376
  end
316
377
 
378
+ # @return [Boolean]
379
+ def respond_to_missing?(meth, *)
380
+ root.respond_to?(meth) || super
381
+ end
382
+
317
383
  # @return [String]
318
384
  def inspect(level=0)
319
385
  ' ' * level + "(#{type}) #{root.inspect(-level)}"
@@ -326,16 +392,34 @@ module RASN1
326
392
  (other.class == self.class) && (other.to_der == self.to_der)
327
393
  end
328
394
 
329
- private
395
+ protected
396
+
397
+ # Give a (nested) element from its name
398
+ # @param [String, Symbol] name
399
+ # @return [Model, Types::Base]
400
+ def by_name(name)
401
+ elt = self[name]
402
+ return elt unless elt.nil?
330
403
 
331
- def is_composed?(el)
332
- [Types::Sequence, Types::Set].include? el.class
404
+ @elements.each_key do |subelt_name|
405
+ if self[subelt_name].is_a?(Model)
406
+ elt = self[subelt_name][name]
407
+ return elt unless elt.nil?
408
+ end
409
+ end
410
+
411
+ nil
333
412
  end
334
413
 
335
- def is_of?(el)
336
- [Types::SequenceOf, Types::SetOf].include? el.class
414
+ private
415
+
416
+ def composed?(elt)
417
+ [Types::Sequence, Types::Set].include? elt.class
337
418
  end
338
419
 
420
+ # proc_or_class:
421
+ # * proc: a Types::Base subclass
422
+ # * class: a model
339
423
  def get_type(proc_or_class, options={})
340
424
  case proc_or_class
341
425
  when Proc
@@ -346,84 +430,87 @@ module RASN1
346
430
  end
347
431
 
348
432
  def generate_root
349
- root = self.class.class_eval { @root }
350
- @root = root[0]
433
+ class_element = self.class.class_eval { @root }
434
+ @root_name = class_element.name
351
435
  @elements = {}
352
- @elements[@root] = get_type(root[1], self.class.options || {})
353
- root
436
+ @elements[@root_name] = get_type(class_element.proc_or_class, self.class.options || {})
437
+ class_element
354
438
  end
355
439
 
356
- def set_elements(name, el, content=nil)
357
- if content.is_a? Array
358
- @elements[name].value = content.map do |name2, proc_or_class, content2|
359
- subel = get_type(proc_or_class)
360
- @elements[name2] = subel
361
- if is_composed?(subel) and content2.is_a? Array
362
- set_elements(name2, proc_or_class, content2)
363
- end
364
- subel
365
- end
440
+ def set_elements(element) # rubocop:disable Naming/AccessorMethodName
441
+ return unless element.content.is_a? Array
442
+
443
+ @elements[name].value = element.content.map do |another_element|
444
+ subel = get_type(another_element.proc_or_class)
445
+ @elements[another_element.name] = subel
446
+ set_elements(another_element) if composed?(subel) && another_element.content.is_a?(Array)
447
+ subel
366
448
  end
367
449
  end
368
450
 
369
451
  def initialize_elements(obj, args)
370
452
  args.each do |name, value|
371
- if obj[name]
372
- if value.is_a? Hash
373
- if obj[name].is_a? Model
374
- initialize_elements obj[name], value
375
- else
376
- raise ArgumentError, "element #{name}: may only pass a Hash for Model elements"
377
- end
378
- elsif value.is_a? Array
379
- composed = if obj[name].is_a? Model
380
- obj[name].root
381
- else
382
- obj[name]
383
- end
384
- if composed.of_type.is_a? Model
385
- value.each do |el|
386
- composed << initialize_elements(composed.of_type.class.new, el)
387
- end
388
- else
389
- value.each do |el|
390
- obj[name] << el
391
- end
392
- end
393
- else
394
- obj[name].value = value
395
- end
453
+ next unless obj[name]
454
+
455
+ case value
456
+ when Hash
457
+ raise ArgumentError, "element #{name}: may only pass a Hash for Model elements" unless obj[name].is_a? Model
458
+
459
+ initialize_elements obj[name], value
460
+ when Array
461
+ initialize_element_from_array(obj[name], value)
462
+ else
463
+ obj[name].value = value
396
464
  end
397
465
  end
398
466
  end
399
467
 
468
+ def initialize_element_from_array(obj, value)
469
+ composed = obj.is_a?(Model) ? obj.root : obj
470
+ if composed.of_type.is_a?(Model)
471
+ value.each do |el|
472
+ composed << initialize_elements(composed.of_type.class.new, el)
473
+ end
474
+ else
475
+ value.each { |el| composed << el }
476
+ end
477
+ end
478
+
400
479
  def private_to_h(element=nil)
401
- my_element = element
402
- my_element = root if my_element.nil?
480
+ my_element = element || root
403
481
  my_element = my_element.root if my_element.is_a?(Model)
404
482
  value = case my_element
405
483
  when Types::SequenceOf
406
- if my_element.of_type < Model
407
- my_element.value.map { |el| el.to_h.values.first }
408
- else
409
- my_element.value.map { |el| private_to_h(el) }
410
- end
484
+ sequence_of_to_h(my_element)
411
485
  when Types::Sequence
412
- seq = my_element.value.map do |el|
413
- next if el.optional? and el.value.nil?
414
- name = el.is_a?(Model) ? @elements.key(el) : el.name
415
- [name, private_to_h(el)]
416
- end
417
- seq.compact!
418
- Hash[seq]
486
+ sequence_to_h(my_element)
419
487
  else
420
488
  my_element.value
421
489
  end
422
490
  if element.nil?
423
- { @root => value }
491
+ { @root_name => value }
424
492
  else
425
493
  value
426
494
  end
427
495
  end
496
+
497
+ def sequence_of_to_h(elt)
498
+ if elt.of_type < Model
499
+ elt.value.map { |el| el.to_h.values.first }
500
+ else
501
+ elt.value.map { |el| private_to_h(el) }
502
+ end
503
+ end
504
+
505
+ def sequence_to_h(seq)
506
+ ary = seq.value.map do |el|
507
+ next if el.optional? && el.value.nil?
508
+
509
+ name = el.is_a?(Model) ? @elements.key(el) : el.name
510
+ [name, private_to_h(el)]
511
+ end
512
+ ary.compact!
513
+ ary.to_h
514
+ end
428
515
  end
429
516
  end
@@ -1,21 +1,23 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 ANY: accepts any types
5
6
  #
6
7
  # If `any#value` is `nil` and Any object is not {#optional?}, `any` will be encoded as a {Null} object.
7
8
  # @author Sylvain Daubert
8
9
  class Any < Base
9
-
10
10
  # @return [String] DER-formated string
11
11
  def to_der
12
- case @value
13
- when Base, Model
14
- @value.to_der
15
- when nil
16
- optional? ? '' : Null.new.to_der
12
+ if value?
13
+ case @value
14
+ when Base, Model
15
+ @value.to_der
16
+ else
17
+ @value.to_s
18
+ end
17
19
  else
18
- @value.to_s
20
+ optional? ? '' : Null.new.to_der
19
21
  end
20
22
  end
21
23
 
@@ -25,30 +27,43 @@ module RASN1
25
27
  # @param [Boolean] ber if +true+, accept BER encoding
26
28
  # @return [Integer] total number of parsed bytes
27
29
  def parse!(der, ber: false)
28
- if der.nil? or der.empty?
30
+ if der.empty?
29
31
  return 0 if optional?
30
32
 
31
- raise ASN1Error, "Expected ANY but get nothing"
33
+ raise ASN1Error, 'Expected ANY but get nothing'
32
34
  end
33
35
 
34
- total_length, = get_data(der, ber)
36
+ id_size = Types.decode_identifier_octets(der).last
37
+ total_length, = get_data(der[id_size..-1], ber)
38
+ total_length += id_size
39
+
40
+ @no_value = false
35
41
  @value = der[0, total_length]
42
+
36
43
  total_length
37
44
  end
38
45
 
39
46
  def inspect(level=0)
40
- str = ''
41
- str << ' ' * level if level > 0
47
+ str = common_inspect(level)
48
+ str << if !value?
49
+ 'NULL'
50
+ elsif @value.is_a?(OctetString) || @value.is_a?(BitString)
51
+ "#{@value.type}: #{value.value.inspect}"
52
+ elsif @value.class < Base
53
+ "#{@value.type}: #{value.value}"
54
+ else
55
+ value.to_s.inspect
56
+ end
57
+ end
58
+
59
+ def common_inspect(level)
60
+ lvl = level >= 0 ? level : 0
61
+ str = ' ' * lvl
42
62
  str << "#{@name} " unless @name.nil?
43
- if @value.nil?
44
- str << "(ANY) NULL"
45
- elsif @value.is_a?(OctetString) or @value.is_a?(BitString)
46
- str << "(ANY) #{@value.type}: #{value.value.inspect}"
47
- elsif @value.class < Base
48
- str << "(ANY) #{@value.type}: #{value.value}"
49
- else
50
- str << "ANY: #{value.to_s.inspect}"
51
- end
63
+ str << asn1_class.to_s.upcase << ' ' unless asn1_class == :universal
64
+ str << "[#{id}] EXPLICIT " if explicit?
65
+ str << "[#{id}] IMPLICIT " if implicit?
66
+ str << '(ANY) '
52
67
  end
53
68
  end
54
69
  end