rasn1 0.6.6 → 0.8.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 +4 -4
- data/README.md +11 -2
- data/lib/rasn1.rb +7 -6
- data/lib/rasn1/model.rb +108 -59
- data/lib/rasn1/types.rb +49 -20
- data/lib/rasn1/types/any.rb +26 -16
- data/lib/rasn1/types/base.rb +146 -115
- data/lib/rasn1/types/bit_string.rb +20 -24
- data/lib/rasn1/types/boolean.rb +17 -14
- data/lib/rasn1/types/choice.rb +11 -16
- data/lib/rasn1/types/constructed.rb +9 -9
- data/lib/rasn1/types/enumerated.rb +4 -2
- data/lib/rasn1/types/generalized_time.rb +24 -26
- data/lib/rasn1/types/ia5string.rb +7 -5
- data/lib/rasn1/types/integer.rb +32 -45
- data/lib/rasn1/types/null.rb +8 -8
- data/lib/rasn1/types/numeric_string.rb +7 -7
- data/lib/rasn1/types/object_id.rb +18 -48
- data/lib/rasn1/types/octet_string.rb +6 -6
- data/lib/rasn1/types/primitive.rb +2 -1
- data/lib/rasn1/types/printable_string.rb +8 -7
- data/lib/rasn1/types/sequence.rb +21 -7
- data/lib/rasn1/types/sequence_of.rb +15 -13
- data/lib/rasn1/types/set.rb +4 -2
- data/lib/rasn1/types/set_of.rb +4 -3
- data/lib/rasn1/types/utc_time.rb +6 -4
- data/lib/rasn1/types/utf8_string.rb +6 -4
- data/lib/rasn1/types/visible_string.rb +4 -3
- data/lib/rasn1/version.rb +3 -1
- metadata +27 -50
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -29
- data/.travis.yml +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -12
- data/rasn1.gemspec +0 -32
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: f4eb9957878c25e54a394fee02aab5a42aa1201ad6250f7f6a2a3992128c87d6
         | 
| 4 | 
            +
              data.tar.gz: 3b3d3c85d619ee7f78923d7f7c839874f329087b0b1a4094839b059daf94c2ec
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 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 | 
            -
            ##  | 
| 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  | 
| 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 | 
            -
                 | 
| 46 | 
            -
                  type = Types. | 
| 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 | 
            -
                     | 
| 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 | 
            -
             | 
| 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 | 
| 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 =  | 
| 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 | 
            -
                                | 
| 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 | 
| 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 =  | 
| 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 | 
            -
                                | 
| 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 =  | 
| 192 | 
            -
                               "    #{prim | 
| 194 | 
            +
                               "  proc = proc do |opts|\n" \
         | 
| 195 | 
            +
                               "    #{prim}.new(options.merge(opts))\n" \
         | 
| 193 196 | 
             
                               "  end\n" \
         | 
| 194 197 | 
             
                               "  @root = [name, proc]\n" \
         | 
| 195 | 
            -
                                | 
| 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 | 
| 205 | 
            -
                    proc =  | 
| 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 | 
| 214 | 
            -
                    proc =  | 
| 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,  | 
| 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 | 
            -
                 | 
| 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 | 
            -
             | 
| 330 | 
            -
                  [Types::Sequence, Types::Set].include? el.class
         | 
| 379 | 
            +
                  nil
         | 
| 331 380 | 
             
                end
         | 
| 332 381 |  | 
| 333 | 
            -
                 | 
| 334 | 
            -
             | 
| 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. | 
| 401 | 
            +
                  @elements[@root] = get_type(root[1], self.class.options || {})
         | 
| 351 402 | 
             
                  root
         | 
| 352 403 | 
             
                end
         | 
| 353 404 |  | 
| 354 | 
            -
                def set_elements(name,  | 
| 355 | 
            -
                   | 
| 356 | 
            -
             | 
| 357 | 
            -
             | 
| 358 | 
            -
             | 
| 359 | 
            -
             | 
| 360 | 
            -
             | 
| 361 | 
            -
             | 
| 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 | 
            -
                     | 
| 370 | 
            -
             | 
| 371 | 
            -
             | 
| 372 | 
            -
             | 
| 373 | 
            -
             | 
| 374 | 
            -
             | 
| 375 | 
            -
             | 
| 376 | 
            -
             | 
| 377 | 
            -
             | 
| 378 | 
            -
             | 
| 379 | 
            -
             | 
| 380 | 
            -
             | 
| 381 | 
            -
             | 
| 382 | 
            -
             | 
| 383 | 
            -
             | 
| 384 | 
            -
             | 
| 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 | 
            -
                         | 
| 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 | 
            -
             | 
| 412 | 
            -
             | 
| 413 | 
            -
             | 
| 414 | 
            -
             | 
| 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
         | 
    
        data/lib/rasn1/types.rb
    CHANGED
    
    | @@ -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 | 
            -
             | 
| 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 | 
            -
             | 
| 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  | 
| 46 | 
            +
                # Give ASN.1 type from a DER string. If ID is unknown, return a {Types::Base}
         | 
| 21 47 | 
             
                # object.
         | 
| 22 | 
            -
                # @param [ | 
| 48 | 
            +
                # @param [String] der
         | 
| 23 49 | 
             
                # @return [Types::Base]
         | 
| 24 50 | 
             
                # @raise [ASN1Error] +tag+ is out of range
         | 
| 25 | 
            -
                def self. | 
| 26 | 
            -
                   | 
| 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 =  | 
| 32 | 
            -
                      next unless type.const_defined? : | 
| 33 | 
            -
             | 
| 56 | 
            +
                    ary = (primitives + constructed).map do |type|
         | 
| 57 | 
            +
                      next unless type.const_defined? :ID
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                      [type::ID, type]
         | 
| 34 60 | 
             
                    end
         | 
| 35 | 
            -
                    @ | 
| 36 | 
            -
                    @ | 
| 61 | 
            +
                    @id2types = Hash[ary]
         | 
| 62 | 
            +
                    @id2types.default = Types::Base
         | 
| 63 | 
            +
                    @id2types.freeze
         | 
| 37 64 | 
             
                  end
         | 
| 38 65 |  | 
| 39 | 
            -
                   | 
| 40 | 
            -
                   | 
| 41 | 
            -
                   | 
| 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] =  | 
| 72 | 
            +
                  options[:tag_value] = id if klass == Types::Base
         | 
| 44 73 | 
             
                  klass.new(options)
         | 
| 45 74 | 
             
                end
         | 
| 46 75 | 
             
              end
         |