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