rasn1 0.6.8 → 0.9.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: 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