rasn1 0.6.6 → 0.8.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: 4acb42db867027d85bfd208aaaee5906af7fe4fdcd92327055f66187415e959f
4
- data.tar.gz: 52cbb1f7306006fb0c03e3e00911d52a474a78c994e2a8e5618046356de43117
3
+ metadata.gz: f4eb9957878c25e54a394fee02aab5a42aa1201ad6250f7f6a2a3992128c87d6
4
+ data.tar.gz: 3b3d3c85d619ee7f78923d7f7c839874f329087b0b1a4094839b059daf94c2ec
5
5
  SHA512:
6
- metadata.gz: ce57ddfbbed465bce5fefb9c7c8b91593e09e4a9cc3797e467e5eb0f482964f1f2bbed3a9053da1f590fddb18e846a1a608351594d667459f59a0f5218294480
7
- data.tar.gz: 93943756f8b526d6467d834976eda477bacd777565d4d46723c050aab5e909b78c5e3f74539999d0cf3ab3c60f316582fc8060814003936df02c09e78b4d7308
6
+ metadata.gz: 9eb501aacf629355e56a4103dfc3e354bfa035c031a23f2dcc61f8add191a13960cd64ea68db36340694f3b20aba316c0088ebe91943d795405b11489be9342e
7
+ data.tar.gz: 115cef9248e5251a24ec0bb6d6e977084cfb4ff78118a7f5be31ee8fea05a0c831b935fa72c18bdc08e71bb729a85cff2b394f4a84aafd56245f70908f0a4dc6
data/README.md CHANGED
@@ -15,13 +15,22 @@ gem 'rasn1'
15
15
 
16
16
  And then execute:
17
17
 
18
- $ bundle
18
+ $ bundle install
19
19
 
20
20
  Or install it yourself as:
21
21
 
22
22
  $ gem install rasn1
23
23
 
24
- ## Usage
24
+ ## Simple usage
25
+
26
+ To decode a DER/BER string without checking a model, do:
27
+
28
+ ```ruby
29
+ decoded_der = RASN1.parse(der_string)
30
+ decoded_ber = RASN1.parse(ber_string, ber: true)
31
+ ```
32
+
33
+ ## Advanced usage
25
34
  All examples below will be based on:
26
35
 
27
36
  ```
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rasn1/version'
2
4
  require 'rasn1/types'
3
5
  require 'rasn1/model'
@@ -5,7 +7,6 @@ require 'rasn1/model'
5
7
  # Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
6
8
  # @author Sylvain Daubert
7
9
  module RASN1
8
-
9
10
  # Base error class
10
11
  class Error < StandardError; end
11
12
 
@@ -26,10 +27,10 @@ module RASN1
26
27
  # CHOICE error: #chosen not set
27
28
  class ChoiceError < RASN1::Error
28
29
  def message
29
- "CHOICE #@name: #chosen not set"
30
+ "CHOICE #{@name}: #chosen not set"
30
31
  end
31
32
  end
32
-
33
+
33
34
  # Parse a DER/BER string without checking a model
34
35
  # @note If you want to check ASN.1 grammary, you should define a {Model}
35
36
  # and use {Model#parse}.
@@ -42,15 +43,15 @@ module RASN1
42
43
  # @return [Types::Base]
43
44
  def self.parse(der, ber: false)
44
45
  root = nil
45
- while der.size > 0
46
- type = Types.tag2type(der[0].ord)
46
+ until der.empty?
47
+ type = Types.id2type(der)
47
48
  type.parse!(der, ber: ber)
48
49
  root = type if root.nil?
49
50
 
50
51
  if [Types::Sequence, Types::Set].include? type.class
51
52
  subder = type.value
52
53
  ary = []
53
- while subder.size > 0
54
+ until subder.empty?
54
55
  ary << self.parse(subder)
55
56
  subder = subder[ary.last.to_der.size..-1]
56
57
  end
@@ -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
@@ -58,8 +59,9 @@ module RASN1
58
59
  # this method.
59
60
  # @author Sylvain Daubert
60
61
  class Model
61
-
62
62
  class << self
63
+ # @return [Hash]
64
+ attr_reader :options
63
65
 
64
66
  # Use another model in this model
65
67
  # @param [String,Symbol] name
@@ -106,16 +108,16 @@ module RASN1
106
108
  # @param [Symbol,String] name name of object in model
107
109
  # @param [Hash] options
108
110
  # @see Types::Choice#initialize
109
- %w(sequence set choice).each do |type|
111
+ %w[sequence set choice].each do |type|
110
112
  class_eval "def #{type}(name, options={})\n" \
111
113
  " options.merge!(name: name)\n" \
112
- " proc = Proc.new do |opts|\n" \
114
+ " proc = proc do |opts|\n" \
113
115
  " Types::#{type.capitalize}.new(options.merge(opts))\n" \
114
116
  " end\n" \
115
117
  " @root = [name, proc]\n" \
116
118
  " @root << options[:content] unless options[:content].nil?\n" \
117
119
  " @root\n" \
118
- "end"
120
+ 'end'
119
121
  end
120
122
 
121
123
  # @method sequence_of(name, type, options)
@@ -128,15 +130,15 @@ module RASN1
128
130
  # @param [Model, Types::Base] type type for SET OF
129
131
  # @param [Hash] options
130
132
  # @see Types::SetOf#initialize
131
- %w(sequence set).each do |type|
133
+ %w[sequence set].each do |type|
132
134
  klass_name = "Types::#{type.capitalize}Of"
133
135
  class_eval "def #{type}_of(name, type, options={})\n" \
134
136
  " options.merge!(name: name)\n" \
135
- " proc = Proc.new do |opts|\n" \
137
+ " proc = proc do |opts|\n" \
136
138
  " #{klass_name}.new(type, options.merge(opts))\n" \
137
139
  " end\n" \
138
140
  " @root = [name, proc]\n" \
139
- "end"
141
+ 'end'
140
142
  end
141
143
 
142
144
  # @method boolean(name, options)
@@ -185,14 +187,15 @@ module RASN1
185
187
  # @see Types::IA5String#initialize
186
188
  Types.primitives.each do |prim|
187
189
  next if prim == Types::ObjectId
190
+
188
191
  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
189
192
  class_eval "def #{method_name}(name, options={})\n" \
190
193
  " options.merge!(name: name)\n" \
191
- " proc = Proc.new do |opts|\n" \
192
- " #{prim.to_s}.new(options.merge(opts))\n" \
194
+ " proc = proc do |opts|\n" \
195
+ " #{prim}.new(options.merge(opts))\n" \
193
196
  " end\n" \
194
197
  " @root = [name, proc]\n" \
195
- "end"
198
+ 'end'
196
199
  end
197
200
 
198
201
  # @param [Symbol,String] name name of object in model
@@ -201,8 +204,8 @@ module RASN1
201
204
  # +Object#object_id+.
202
205
  # @see Types::ObjectId#initialize
203
206
  def objectid(name, options={})
204
- options.merge!(name: name)
205
- proc = Proc.new { |opts| Types::ObjectId.new(options.merge(opts)) }
207
+ options[:name] = name
208
+ proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) }
206
209
  @root = [name, proc]
207
210
  end
208
211
 
@@ -210,15 +213,16 @@ module RASN1
210
213
  # @param [Hash] options
211
214
  # @see Types::Any#initialize
212
215
  def any(name, options={})
213
- options.merge!(name: name)
214
- proc = Proc.new { |opts| Types::Any.new(options.merge(opts)) }
216
+ options[:name] = name
217
+ proc = proc { |opts| Types::Any.new(options.merge(opts)) }
215
218
  @root = [name, proc]
216
219
  end
217
220
 
218
221
  # Give type name (aka class name)
219
222
  # @return [String]
220
223
  def type
221
- return @type if @type
224
+ return @type if defined? @type
225
+
222
226
  @type = self.to_s.gsub(/.*::/, '')
223
227
  end
224
228
 
@@ -254,7 +258,8 @@ module RASN1
254
258
  # @param [Object] value
255
259
  # @return [Object] value
256
260
  def []=(name, value)
257
- raise Error, "cannot set value for a Model" if @elements[name].is_a? Model
261
+ raise Error, 'cannot set value for a Model' if @elements[name].is_a? Model
262
+
258
263
  @elements[name].value = value
259
264
  end
260
265
 
@@ -302,6 +307,32 @@ module RASN1
302
307
  @elements[@root].parse!(str.dup.force_encoding('BINARY'), ber: ber)
303
308
  end
304
309
 
310
+ # @overload value
311
+ # Get value of root element
312
+ # @return [Object,nil]
313
+ # @overload value(name)
314
+ # Direct access to the value of +name+ (nested) element of model.
315
+ # @param [String,Symbol] name
316
+ # @return [Object,nil]
317
+ # @return [Object,nil]
318
+ def value(name=nil, *args)
319
+ if name.nil?
320
+ @elements[@root].value
321
+ else
322
+ elt = by_name(name)
323
+
324
+ unless args.empty?
325
+ elt = elt.root if elt.is_a?(Model)
326
+ args.each do |arg|
327
+ elt = elt.root if elt.is_a?(Model)
328
+ elt = elt[arg]
329
+ end
330
+ end
331
+
332
+ elt.value
333
+ end
334
+ end
335
+
305
336
  # Delegate some methods to root element
306
337
  # @param [Symbol] meth
307
338
  def method_missing(meth, *args)
@@ -312,6 +343,11 @@ module RASN1
312
343
  end
313
344
  end
314
345
 
346
+ # @return [Boolean]
347
+ def respond_to_missing?(meth, *)
348
+ @elements[@root].respond_to?(meth) || super
349
+ end
350
+
315
351
  # @return [String]
316
352
  def inspect(level=0)
317
353
  ' ' * level + "(#{type}) #{root.inspect(-level)}"
@@ -324,14 +360,29 @@ module RASN1
324
360
  (other.class == self.class) && (other.to_der == self.to_der)
325
361
  end
326
362
 
327
- private
363
+ protected
364
+
365
+ # Give a (nested) element from its name
366
+ # @param [String, Symbol] name
367
+ # @return [Model, Types::Base]
368
+ def by_name(name)
369
+ elt = self[name]
370
+ return elt unless elt.nil?
371
+
372
+ @elements.each_key do |subelt_name|
373
+ if self[subelt_name].is_a?(Model)
374
+ elt = self[subelt_name][name]
375
+ return elt unless elt.nil?
376
+ end
377
+ end
328
378
 
329
- def is_composed?(el)
330
- [Types::Sequence, Types::Set].include? el.class
379
+ nil
331
380
  end
332
381
 
333
- def is_of?(el)
334
- [Types::SequenceOf, Types::SetOf].include? el.class
382
+ private
383
+
384
+ def composed?(elt)
385
+ [Types::Sequence, Types::Set].include? elt.class
335
386
  end
336
387
 
337
388
  def get_type(proc_or_class, options={})
@@ -347,50 +398,47 @@ module RASN1
347
398
  root = self.class.class_eval { @root }
348
399
  @root = root[0]
349
400
  @elements = {}
350
- @elements[@root] = get_type(root[1], self.class.class_eval { @options } || {})
401
+ @elements[@root] = get_type(root[1], self.class.options || {})
351
402
  root
352
403
  end
353
404
 
354
- def set_elements(name, el, content=nil)
355
- if content.is_a? Array
356
- @elements[name].value = content.map do |name2, proc_or_class, content2|
357
- subel = get_type(proc_or_class)
358
- @elements[name2] = subel
359
- if is_composed?(subel) and content2.is_a? Array
360
- set_elements(name2, proc_or_class, content2)
361
- end
362
- subel
363
- end
405
+ def set_elements(name, _elt, content=nil)
406
+ return unless content.is_a? Array
407
+
408
+ @elements[name].value = content.map do |name2, proc_or_class, content2|
409
+ subel = get_type(proc_or_class)
410
+ @elements[name2] = subel
411
+ set_elements(name2, proc_or_class, content2) if composed?(subel) && content2.is_a?(Array)
412
+ subel
364
413
  end
365
414
  end
366
415
 
367
416
  def initialize_elements(obj, args)
368
417
  args.each do |name, value|
369
- if obj[name]
370
- if value.is_a? Hash
371
- if obj[name].is_a? Model
372
- initialize_elements obj[name], value
373
- else
374
- raise ArgumentError, "element #{name}: may only pass a Hash for Model elements"
375
- end
376
- elsif value.is_a? Array
377
- composed = if obj[name].is_a? Model
378
- obj[name].root
379
- else
380
- obj[name]
381
- end
382
- if composed.of_type.is_a? Model
383
- value.each do |el|
384
- composed << initialize_elements(composed.of_type.class.new, el)
385
- end
386
- else
387
- value.each do |el|
388
- obj[name] << el
389
- end
418
+ next unless obj[name]
419
+
420
+ case value
421
+ when Hash
422
+ raise ArgumentError, "element #{name}: may only pass a Hash for Model elements" unless obj[name].is_a? Model
423
+
424
+ initialize_elements obj[name], value
425
+ when Array
426
+ composed = if obj[name].is_a? Model
427
+ obj[name].root
428
+ else
429
+ obj[name]
430
+ end
431
+ if composed.of_type.is_a? Model
432
+ value.each do |el|
433
+ composed << initialize_elements(composed.of_type.class.new, el)
390
434
  end
391
435
  else
392
- obj[name].value = value
436
+ value.each do |el|
437
+ obj[name] << el
438
+ end
393
439
  end
440
+ else
441
+ obj[name].value = value
394
442
  end
395
443
  end
396
444
  end
@@ -408,10 +456,11 @@ module RASN1
408
456
  end
409
457
  when Types::Sequence
410
458
  seq = my_element.value.map do |el|
411
- next if el.optional? and el.value.nil?
412
- name = el.is_a?(Model) ? @elements.key(el) : el.name
413
- [name, private_to_h(el)]
414
- end
459
+ next if el.optional? && el.value.nil?
460
+
461
+ name = el.is_a?(Model) ? @elements.key(el) : el.name
462
+ [name, private_to_h(el)]
463
+ end
415
464
  seq.compact!
416
465
  Hash[seq]
417
466
  else
@@ -1,46 +1,75 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  # This modules is a namesapce for all ASN.1 type classes.
3
5
  # @author Sylvain Daubert
4
6
  module Types
5
-
6
7
  # Give all primitive types
7
8
  # @return [Array<Types::Primitive>]
8
9
  def self.primitives
9
- @primitives ||= self.constants.map { |c| Types.const_get(c) }.
10
- select { |klass| klass < Primitive }
10
+ @primitives ||= self.constants.map { |c| Types.const_get(c) }
11
+ .select { |klass| klass < Primitive }
11
12
  end
12
13
 
13
14
  # Give all constructed types
14
15
  # @return [Array<Types::Constructed>]
15
16
  def self.constructed
16
- @constructed ||= self.constants.map { |c| Types.const_get(c) }.
17
- select { |klass| klass < Constructed }
17
+ @constructed ||= self.constants.map { |c| Types.const_get(c) }
18
+ .select { |klass| klass < Constructed }
19
+ end
20
+
21
+ # @private
22
+ # Decode a DER string to extract identifier octets.
23
+ # @param [String] der
24
+ # @return [Array] Return ASN.1 class as Symbol, contructed/primitive as Symbol,
25
+ # ID and size of identifier octets
26
+ def self.decode_identifier_octets(der)
27
+ first_octet = der[0].unpack1('C')
28
+ asn1_class = Types::Base::CLASSES.key(first_octet & Types::Base::CLASS_MASK)
29
+ pc = (first_octet & Types::Constructed::ASN1_PC).positive? ? :constructed : :primitive
30
+ id = first_octet & Types::Base::MULTI_OCTETS_ID
31
+
32
+ size = if id == Types::Base::MULTI_OCTETS_ID
33
+ id = 0
34
+ 1.upto(der.size - 1) do |i|
35
+ octet = der[i].unpack1('C')
36
+ id = (id << 7) | (octet & 0x7f)
37
+ break i + 1 if (octet & 0x80).zero?
38
+ end
39
+ else
40
+ 1
41
+ end
42
+
43
+ [asn1_class, pc, id, size]
18
44
  end
19
45
 
20
- # Give ASN.1 type from an integer. If +tag+ is unknown, return a {Types::Base}
46
+ # Give ASN.1 type from a DER string. If ID is unknown, return a {Types::Base}
21
47
  # object.
22
- # @param [Integer] tag
48
+ # @param [String] der
23
49
  # @return [Types::Base]
24
50
  # @raise [ASN1Error] +tag+ is out of range
25
- def self.tag2type(tag)
26
- raise ASN1Error, "tag is out of range" if tag > 0xff
27
-
28
- if !defined? @tag2types
51
+ def self.id2type(der)
52
+ # Define a cache for well-known ASN.1 types
53
+ unless defined? @id2types
29
54
  constructed = self.constructed - [Types::SequenceOf, Types::SetOf]
30
55
  primitives = self.primitives - [Types::Enumerated]
31
- ary = [primitives, constructed].flatten.map do |type|
32
- next unless type.const_defined? :TAG
33
- [type::TAG, type]
56
+ ary = (primitives + constructed).map do |type|
57
+ next unless type.const_defined? :ID
58
+
59
+ [type::ID, type]
34
60
  end
35
- @tag2types = Hash[ary]
36
- @tag2types.default = Types::Base
61
+ @id2types = Hash[ary]
62
+ @id2types.default = Types::Base
63
+ @id2types.freeze
37
64
  end
38
65
 
39
- klass = @tag2types[tag & 0xdf] # Remove CONSTRUCTED bit
40
- is_constructed = (tag & 0x20) == 0x20
41
- asn1class = Types::Base::CLASSES.key(tag & 0xc0)
66
+ asn1class, pc, id, = self.decode_identifier_octets(der)
67
+ # cache_id: check versus class and 5 LSB bits
68
+ cache_id = der.unpack1('C') & 0xdf
69
+ klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::Base
70
+ is_constructed = (pc == :constructed)
42
71
  options = { class: asn1class, constructed: is_constructed }
43
- options[:tag_value] = (tag & 0x1f) if klass == Types::Base
72
+ options[:tag_value] = id if klass == Types::Base
44
73
  klass.new(options)
45
74
  end
46
75
  end