rasn1 0.6.8 → 0.7.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: 7fc411854088bf4835982774db3a4037c1760c9cb2088653fb2dc92d82d7788c
4
+ data.tar.gz: e81586c34344dfa3c3c26868e6689ebf841c964eff6c80dd0705288617818143
5
5
  SHA512:
6
- metadata.gz: 4dc47229a202f0bfa7d56a129cd5e1807009b9147da6e844d5a47589fbf0e8eab9bebbb51f81866a0222eb4a6dbf953d381f266ec332e697b6caf1b894fa6c18
7
- data.tar.gz: 4fc5127882d478d111452dff0cac6e226d270f47b4798b6a365f735c0bb1402ec417640b48be9839f7336fcf6d526f2a771d90bdc6ed64b0cefc3258d9e22bff
6
+ metadata.gz: aecf47fe0ebb5d79546cf63c514359d4477fbd0c3a543aaebe1619f45cd4f8604beeff8ca614a59444a68ddc6aa8901ead9160b3cb7026ead301ea2d6935ed27
7
+ data.tar.gz: edea58f24e5931deb04a5f038d8c9f0b2945bb4c90892acfa264b29c9f530b0e6534b25a41173a57fd002f7da7bfa52f1820d172dfe91ddaf4257a73df5ab9d1
data/.rubocop.yml CHANGED
@@ -6,6 +6,8 @@ Lint/Void:
6
6
  Enabled: false
7
7
  Metrics:
8
8
  Enabled: false
9
+ Naming/AccessorMethodName:
10
+ Enabled: false
9
11
  Style/AsciiComments:
10
12
  Enabled: false
11
13
  Style/Encoding:
@@ -20,6 +22,8 @@ Style/PerlBackrefs:
20
22
  Enabled: false
21
23
  Style/RedundantSelf:
22
24
  Enabled: false
25
+ Style/RegexpLiteral:
26
+ EnforcedStyle: slashes
23
27
  Style/StructInheritance:
24
28
  Enabled: false
25
29
  Style/TrailingCommaInArrayLiteral:
data/.travis.yml CHANGED
@@ -3,7 +3,8 @@ rvm:
3
3
  - 2.3
4
4
  - 2.4
5
5
  - 2.5
6
+ - 2.6
6
7
  install:
7
- - bundle install --path vendor/bundle --jobs=3 --retry=3
8
- script:
9
- - bundle exec rake
8
+ - gem install bundler --version "~> 1.17.3"
9
+ - bundle _1.17.3_ install --path vendor/bundle --jobs=3 --retry=3
10
+
data/Changelog.md ADDED
@@ -0,0 +1,11 @@
1
+ # Rasn1 Changelog
2
+
3
+ ## 0.7.0
4
+
5
+ * add RASN1::Model#value to get value of a (potentially nested) element.
6
+ * add RASN1::Types::Sequence#[]. Access to element by index or by name.
7
+ * add frozen_string literal on all ruby files.
8
+ * optimize RASN1::Types.tag2type.
9
+ * refactoring of RASN1::Types::Base and RASN1::Types::Boolean.
10
+ * fix bugs:
11
+ * RASN1::Types::Base#initialize_copy raises on ruby 2.3 when @value and/or @default were nil, true, false of Integer.
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
  ```
data/lib/rasn1.rb CHANGED
@@ -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,7 +43,7 @@ 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
+ until der.empty?
46
47
  type = Types.tag2type(der[0].ord)
47
48
  type.parse!(der, ber: ber)
48
49
  root = type if root.nil?
@@ -50,7 +51,7 @@ module RASN1
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
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
@@ -58,7 +59,6 @@ module RASN1
58
59
  # this method.
59
60
  # @author Sylvain Daubert
60
61
  class Model
61
-
62
62
  class << self
63
63
  # @return [Hash]
64
64
  attr_reader :options
@@ -92,7 +92,7 @@ module RASN1
92
92
  # @return [void]
93
93
  def inherited(klass)
94
94
  super
95
- root = defined?(@root )? @root : nil
95
+ root = @root
96
96
  klass.class_eval { @root = root }
97
97
  end
98
98
 
@@ -108,16 +108,16 @@ module RASN1
108
108
  # @param [Symbol,String] name name of object in model
109
109
  # @param [Hash] options
110
110
  # @see Types::Choice#initialize
111
- %w(sequence set choice).each do |type|
111
+ %w[sequence set choice].each do |type|
112
112
  class_eval "def #{type}(name, options={})\n" \
113
113
  " options.merge!(name: name)\n" \
114
- " proc = Proc.new do |opts|\n" \
114
+ " proc = proc do |opts|\n" \
115
115
  " Types::#{type.capitalize}.new(options.merge(opts))\n" \
116
116
  " end\n" \
117
117
  " @root = [name, proc]\n" \
118
118
  " @root << options[:content] unless options[:content].nil?\n" \
119
119
  " @root\n" \
120
- "end"
120
+ 'end'
121
121
  end
122
122
 
123
123
  # @method sequence_of(name, type, options)
@@ -130,15 +130,15 @@ module RASN1
130
130
  # @param [Model, Types::Base] type type for SET OF
131
131
  # @param [Hash] options
132
132
  # @see Types::SetOf#initialize
133
- %w(sequence set).each do |type|
133
+ %w[sequence set].each do |type|
134
134
  klass_name = "Types::#{type.capitalize}Of"
135
135
  class_eval "def #{type}_of(name, type, options={})\n" \
136
136
  " options.merge!(name: name)\n" \
137
- " proc = Proc.new do |opts|\n" \
137
+ " proc = proc do |opts|\n" \
138
138
  " #{klass_name}.new(type, options.merge(opts))\n" \
139
139
  " end\n" \
140
140
  " @root = [name, proc]\n" \
141
- "end"
141
+ 'end'
142
142
  end
143
143
 
144
144
  # @method boolean(name, options)
@@ -187,14 +187,15 @@ module RASN1
187
187
  # @see Types::IA5String#initialize
188
188
  Types.primitives.each do |prim|
189
189
  next if prim == Types::ObjectId
190
+
190
191
  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
191
192
  class_eval "def #{method_name}(name, options={})\n" \
192
193
  " options.merge!(name: name)\n" \
193
- " proc = Proc.new do |opts|\n" \
194
- " #{prim.to_s}.new(options.merge(opts))\n" \
194
+ " proc = proc do |opts|\n" \
195
+ " #{prim}.new(options.merge(opts))\n" \
195
196
  " end\n" \
196
197
  " @root = [name, proc]\n" \
197
- "end"
198
+ 'end'
198
199
  end
199
200
 
200
201
  # @param [Symbol,String] name name of object in model
@@ -204,7 +205,7 @@ module RASN1
204
205
  # @see Types::ObjectId#initialize
205
206
  def objectid(name, options={})
206
207
  options.merge!(name: name)
207
- proc = Proc.new { |opts| Types::ObjectId.new(options.merge(opts)) }
208
+ proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) }
208
209
  @root = [name, proc]
209
210
  end
210
211
 
@@ -213,7 +214,7 @@ module RASN1
213
214
  # @see Types::Any#initialize
214
215
  def any(name, options={})
215
216
  options.merge!(name: name)
216
- proc = Proc.new { |opts| Types::Any.new(options.merge(opts)) }
217
+ proc = proc { |opts| Types::Any.new(options.merge(opts)) }
217
218
  @root = [name, proc]
218
219
  end
219
220
 
@@ -221,6 +222,7 @@ module RASN1
221
222
  # @return [String]
222
223
  def type
223
224
  return @type if defined? @type
225
+
224
226
  @type = self.to_s.gsub(/.*::/, '')
225
227
  end
226
228
 
@@ -256,7 +258,8 @@ module RASN1
256
258
  # @param [Object] value
257
259
  # @return [Object] value
258
260
  def []=(name, value)
259
- 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
+
260
263
  @elements[name].value = value
261
264
  end
262
265
 
@@ -304,6 +307,32 @@ module RASN1
304
307
  @elements[@root].parse!(str.dup.force_encoding('BINARY'), ber: ber)
305
308
  end
306
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
+
307
336
  # Delegate some methods to root element
308
337
  # @param [Symbol] meth
309
338
  def method_missing(meth, *args)
@@ -314,6 +343,11 @@ module RASN1
314
343
  end
315
344
  end
316
345
 
346
+ # @return [Boolean]
347
+ def respond_to_missing?(meth, *)
348
+ @elements[@root].respond_to?(meth) || super
349
+ end
350
+
317
351
  # @return [String]
318
352
  def inspect(level=0)
319
353
  ' ' * level + "(#{type}) #{root.inspect(-level)}"
@@ -326,14 +360,29 @@ module RASN1
326
360
  (other.class == self.class) && (other.to_der == self.to_der)
327
361
  end
328
362
 
329
- 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
+ keys.each 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
330
378
 
331
- def is_composed?(el)
332
- [Types::Sequence, Types::Set].include? el.class
379
+ nil
333
380
  end
334
381
 
335
- def is_of?(el)
336
- [Types::SequenceOf, Types::SetOf].include? el.class
382
+ private
383
+
384
+ def composed?(elt)
385
+ [Types::Sequence, Types::Set].include? elt.class
337
386
  end
338
387
 
339
388
  def get_type(proc_or_class, options={})
@@ -353,16 +402,16 @@ module RASN1
353
402
  root
354
403
  end
355
404
 
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
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
+ if composed?(subel) && content2.is_a?(Array)
412
+ set_elements(name2, proc_or_class, content2)
365
413
  end
414
+ subel
366
415
  end
367
416
  end
368
417
 
@@ -410,10 +459,11 @@ module RASN1
410
459
  end
411
460
  when Types::Sequence
412
461
  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
462
+ next if el.optional? && el.value.nil?
463
+
464
+ name = el.is_a?(Model) ? @elements.key(el) : el.name
465
+ [name, private_to_h(el)]
466
+ end
417
467
  seq.compact!
418
468
  Hash[seq]
419
469
  else
data/lib/rasn1/types.rb CHANGED
@@ -1,20 +1,21 @@
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 }
18
19
  end
19
20
 
20
21
  # Give ASN.1 type from an integer. If +tag+ is unknown, return a {Types::Base}
@@ -23,17 +24,19 @@ module RASN1
23
24
  # @return [Types::Base]
24
25
  # @raise [ASN1Error] +tag+ is out of range
25
26
  def self.tag2type(tag)
26
- raise ASN1Error, "tag is out of range" if tag > 0xff
27
+ raise ASN1Error, 'tag is out of range' if tag > 0xff
27
28
 
28
- if !defined? @tag2types
29
+ unless defined? @tag2types
29
30
  constructed = self.constructed - [Types::SequenceOf, Types::SetOf]
30
31
  primitives = self.primitives - [Types::Enumerated]
31
- ary = [primitives, constructed].flatten.map do |type|
32
+ ary = (primitives + constructed).map do |type|
32
33
  next unless type.const_defined? :TAG
34
+
33
35
  [type::TAG, type]
34
36
  end
35
37
  @tag2types = Hash[ary]
36
38
  @tag2types.default = Types::Base
39
+ @tag2types.freeze
37
40
  end
38
41
 
39
42
  klass = @tag2types[tag & 0xdf] # Remove CONSTRUCTED bit
@@ -1,12 +1,12 @@
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
12
  case @value
@@ -25,30 +25,30 @@ module RASN1
25
25
  # @param [Boolean] ber if +true+, accept BER encoding
26
26
  # @return [Integer] total number of parsed bytes
27
27
  def parse!(der, ber: false)
28
- if der.nil? or der.empty?
28
+ if der.nil? || der.empty?
29
29
  return 0 if optional?
30
30
 
31
- raise ASN1Error, "Expected ANY but get nothing"
31
+ raise ASN1Error, 'Expected ANY but get nothing'
32
32
  end
33
33
 
34
- total_length, = get_data(der, ber)
34
+ total_length, = get_data(der, ber)
35
35
  @value = der[0, total_length]
36
36
  total_length
37
37
  end
38
38
 
39
39
  def inspect(level=0)
40
- str = ''
41
- str << ' ' * level if level > 0
40
+ lvl = level >= 0 ? level : 0
41
+ str = ' ' * lvl
42
42
  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
43
+ str << if @value.nil?
44
+ '(ANY) NULL'
45
+ elsif @value.is_a?(OctetString) || @value.is_a?(BitString)
46
+ "(ANY) #{@value.type}: #{value.value.inspect}"
47
+ elsif @value.class < Base
48
+ "(ANY) #{@value.type}: #{value.value}"
49
+ else
50
+ "ANY: #{value.to_s.inspect}"
51
+ end
52
52
  end
53
53
  end
54
54
  end