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
    
        data/lib/rasn1/types/any.rb
    CHANGED
    
    | @@ -1,19 +1,19 @@ | |
| 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 | 
            -
                # If `any#value` is `nil | 
| 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
         | 
| 13 13 | 
             
                    when Base, Model
         | 
| 14 14 | 
             
                      @value.to_der
         | 
| 15 15 | 
             
                    when nil
         | 
| 16 | 
            -
                      Null.new.to_der
         | 
| 16 | 
            +
                      optional? ? '' : Null.new.to_der
         | 
| 17 17 | 
             
                    else
         | 
| 18 18 | 
             
                      @value.to_s
         | 
| 19 19 | 
             
                    end
         | 
| @@ -25,24 +25,34 @@ 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 | 
            -
                     | 
| 28 | 
            +
                    if der.nil? || der.empty?
         | 
| 29 | 
            +
                      return 0 if optional?
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                      raise ASN1Error, 'Expected ANY but get nothing'
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    id_size = Types.decode_identifier_octets(der).last
         | 
| 35 | 
            +
                    total_length, = get_data(der[id_size..-1], ber)
         | 
| 36 | 
            +
                    total_length += id_size
         | 
| 37 | 
            +
             | 
| 29 38 | 
             
                    @value = der[0, total_length]
         | 
| 39 | 
            +
             | 
| 30 40 | 
             
                    total_length
         | 
| 31 41 | 
             
                  end
         | 
| 32 42 |  | 
| 33 43 | 
             
                  def inspect(level=0)
         | 
| 34 | 
            -
                     | 
| 35 | 
            -
                    str  | 
| 44 | 
            +
                    lvl = level >= 0 ? level : 0
         | 
| 45 | 
            +
                    str = '  ' * lvl
         | 
| 36 46 | 
             
                    str << "#{@name} " unless @name.nil?
         | 
| 37 | 
            -
                    if @value.nil?
         | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 47 | 
            +
                    str << if @value.nil?
         | 
| 48 | 
            +
                             '(ANY) NULL'
         | 
| 49 | 
            +
                           elsif @value.is_a?(OctetString) || @value.is_a?(BitString)
         | 
| 50 | 
            +
                             "(ANY) #{@value.type}: #{value.value.inspect}"
         | 
| 51 | 
            +
                           elsif @value.class < Base
         | 
| 52 | 
            +
                             "(ANY) #{@value.type}: #{value.value}"
         | 
| 53 | 
            +
                           else
         | 
| 54 | 
            +
                             "ANY: #{value.to_s.inspect}"
         | 
| 55 | 
            +
                           end
         | 
| 46 56 | 
             
                  end
         | 
| 47 57 | 
             
                end
         | 
| 48 58 | 
             
              end
         | 
    
        data/lib/rasn1/types/base.rb
    CHANGED
    
    | @@ -1,10 +1,11 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            module RASN1
         | 
| 2 4 | 
             
              module Types
         | 
| 3 | 
            -
             | 
| 4 5 | 
             
                # @abstract This is base class for all ASN.1 types.
         | 
| 5 6 | 
             
                #
         | 
| 6 7 | 
             
                #   Subclasses SHOULD define:
         | 
| 7 | 
            -
                #   *  | 
| 8 | 
            +
                #   * an ID constant defining ASN.1 BER/DER identification number,
         | 
| 8 9 | 
             
                #   * a private method {#value_to_der} converting its {#value} to DER,
         | 
| 9 10 | 
             
                #   * a private method {#der_to_value} converting DER into {#value}.
         | 
| 10 11 | 
             
                #
         | 
| @@ -12,15 +13,15 @@ module RASN1 | |
| 12 13 | 
             
                # An optional value may be defined using +:optional+ key from {#initialize}:
         | 
| 13 14 | 
             
                #   Integer.new(:int, optional: true)
         | 
| 14 15 | 
             
                # An optional value implies:
         | 
| 15 | 
            -
                # * while parsing, if decoded  | 
| 16 | 
            -
                #   is raised, and parser tries  | 
| 16 | 
            +
                # * while parsing, if decoded ID is not optional expected ID, no {ASN1Error}
         | 
| 17 | 
            +
                #   is raised, and parser tries next field,
         | 
| 17 18 | 
             
                # * while encoding, if {#value} is +nil+, this value is not encoded.
         | 
| 18 19 | 
             
                # ==Define a default value
         | 
| 19 20 | 
             
                # A default value may be defined using +:default+ key from {#initialize}:
         | 
| 20 21 | 
             
                #  Integer.new(:int, default: 0)
         | 
| 21 22 | 
             
                # A default value implies:
         | 
| 22 | 
            -
                # * while parsing, if decoded  | 
| 23 | 
            -
                #   and parser sets default value to this  | 
| 23 | 
            +
                # * while parsing, if decoded ID is not expected one, no {ASN1Error} is raised
         | 
| 24 | 
            +
                #   and parser sets default value to this ID. Then parser tries next field,
         | 
| 24 25 | 
             
                # * while encoding, if {#value} is equal to default value, this value is not
         | 
| 25 26 | 
             
                #   encoded.
         | 
| 26 27 | 
             
                # ==Define a tagged value
         | 
| @@ -44,16 +45,20 @@ module RASN1 | |
| 44 45 | 
             
                #  ctype_implicit = RASN1::Types::Integer.new(implicit: 0)
         | 
| 45 46 | 
             
                # @author Sylvain Daubert
         | 
| 46 47 | 
             
                class Base
         | 
| 47 | 
            -
                  # Allowed ASN.1  | 
| 48 | 
            +
                  # Allowed ASN.1 classes
         | 
| 48 49 | 
             
                  CLASSES = {
         | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 50 | 
            +
                    universal: 0x00,
         | 
| 51 | 
            +
                    application: 0x40,
         | 
| 52 | 
            +
                    context: 0x80,
         | 
| 53 | 
            +
                    private: 0xc0
         | 
| 54 | 
            +
                  }.freeze
         | 
| 54 55 |  | 
| 55 | 
            -
                  #  | 
| 56 | 
            -
                   | 
| 56 | 
            +
                  # Binary mask to get class
         | 
| 57 | 
            +
                  # @private
         | 
| 58 | 
            +
                  CLASS_MASK = 0xc0
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                  # @private first octet identifier for multi-octets identifier
         | 
| 61 | 
            +
                  MULTI_OCTETS_ID = 0x1f
         | 
| 57 62 |  | 
| 58 63 | 
             
                  # Length value for indefinite length
         | 
| 59 64 | 
             
                  INDEFINITE_LENGTH = 0x80
         | 
| @@ -70,13 +75,14 @@ module RASN1 | |
| 70 75 | 
             
                  # Get ASN.1 type
         | 
| 71 76 | 
             
                  # @return [String]
         | 
| 72 77 | 
             
                  def self.type
         | 
| 73 | 
            -
                    return @type if @type
         | 
| 78 | 
            +
                    return @type if defined? @type
         | 
| 79 | 
            +
             | 
| 74 80 | 
             
                    @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
         | 
| 75 81 | 
             
                  end
         | 
| 76 82 |  | 
| 77 83 | 
             
                  # Get ASN.1 type used to encode this one
         | 
| 78 84 | 
             
                  # @return [String]
         | 
| 79 | 
            -
                  def self. | 
| 85 | 
            +
                  def self.encoded_type
         | 
| 80 86 | 
             
                    type
         | 
| 81 87 | 
             
                  end
         | 
| 82 88 |  | 
| @@ -91,14 +97,13 @@ module RASN1 | |
| 91 97 | 
             
                    obj
         | 
| 92 98 | 
             
                  end
         | 
| 93 99 |  | 
| 94 | 
            -
             | 
| 95 100 | 
             
                  # @overload initialize(options={})
         | 
| 96 101 | 
             
                  #   @param [Hash] options
         | 
| 97 | 
            -
                  #   @option options [Symbol] :class ASN.1  | 
| 102 | 
            +
                  #   @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
         | 
| 98 103 | 
             
                  #    If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
         | 
| 99 104 | 
             
                  #   @option options [::Boolean] :optional define this tag as optional. Default
         | 
| 100 105 | 
             
                  #     is +false+
         | 
| 101 | 
            -
                  #   @option options [Object] :default default value  | 
| 106 | 
            +
                  #   @option options [Object] :default default value (ASN.1 DEFAULT)
         | 
| 102 107 | 
             
                  #   @option options [Object] :value value to set
         | 
| 103 108 | 
             
                  #   @option options [::Integer] :implicit define an IMPLICIT tagged type
         | 
| 104 109 | 
             
                  #   @option options [::Integer] :explicit define an EXPLICIT tagged type
         | 
| @@ -108,11 +113,11 @@ module RASN1 | |
| 108 113 | 
             
                  # @overload initialize(value, options={})
         | 
| 109 114 | 
             
                  #   @param [Object] value value to set for this ASN.1 object
         | 
| 110 115 | 
             
                  #   @param [Hash] options
         | 
| 111 | 
            -
                  #   @option options [Symbol] :class ASN.1  | 
| 116 | 
            +
                  #   @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
         | 
| 112 117 | 
             
                  #    If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
         | 
| 113 | 
            -
                  #   @option options [::Boolean] :optional define this  | 
| 118 | 
            +
                  #   @option options [::Boolean] :optional define this value as optional. Default
         | 
| 114 119 | 
             
                  #     is +false+
         | 
| 115 | 
            -
                  #   @option options [Object] :default default value  | 
| 120 | 
            +
                  #   @option options [Object] :default default value (ASN.1 DEFAULT)
         | 
| 116 121 | 
             
                  #   @option options [::Integer] :implicit define an IMPLICIT tagged type
         | 
| 117 122 | 
             
                  #   @option options [::Integer] :explicit define an EXPLICIT tagged type
         | 
| 118 123 | 
             
                  #   @option options [::Boolean] :constructed if +true+, set type as constructed.
         | 
| @@ -128,20 +133,10 @@ module RASN1 | |
| 128 133 | 
             
                    end
         | 
| 129 134 | 
             
                  end
         | 
| 130 135 |  | 
| 131 | 
            -
                  # Used by +#dup+ and +#clone+. Deep copy @value.
         | 
| 132 | 
            -
                  def initialize_copy( | 
| 133 | 
            -
                    @value =  | 
| 134 | 
            -
             | 
| 135 | 
            -
                               @value
         | 
| 136 | 
            -
                             else
         | 
| 137 | 
            -
                               @value.dup
         | 
| 138 | 
            -
                             end
         | 
| 139 | 
            -
                    @default = case
         | 
| 140 | 
            -
                             when NilClass, TrueClass, FalseClass, Integer
         | 
| 141 | 
            -
                               @default
         | 
| 142 | 
            -
                             else
         | 
| 143 | 
            -
                               @default.dup
         | 
| 144 | 
            -
                             end
         | 
| 136 | 
            +
                  # Used by +#dup+ and +#clone+. Deep copy @value and @default.
         | 
| 137 | 
            +
                  def initialize_copy(_other)
         | 
| 138 | 
            +
                    @value = @value.dup
         | 
| 139 | 
            +
                    @default = @default.dup
         | 
| 145 140 | 
             
                  end
         | 
| 146 141 |  | 
| 147 142 | 
             
                  # Get value or default value
         | 
| @@ -168,21 +163,21 @@ module RASN1 | |
| 168 163 | 
             
                  # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
         | 
| 169 164 | 
             
                  #   if explicit, else +false+
         | 
| 170 165 | 
             
                  def explicit?
         | 
| 171 | 
            -
                    @tag | 
| 166 | 
            +
                    !defined?(@tag) ? nil : @tag == :explicit
         | 
| 172 167 | 
             
                  end
         | 
| 173 168 |  | 
| 174 169 | 
             
                  # Say if a tagged type is implicit
         | 
| 175 170 | 
             
                  # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
         | 
| 176 171 | 
             
                  #   if implicit, else +false+
         | 
| 177 172 | 
             
                  def implicit?
         | 
| 178 | 
            -
                    @tag | 
| 173 | 
            +
                    !defined?(@tag) ? nil : @tag == :implicit
         | 
| 179 174 | 
             
                  end
         | 
| 180 175 |  | 
| 181 176 | 
             
                  # @abstract This method SHOULD be partly implemented by subclasses, which
         | 
| 182 177 | 
             
                  #   SHOULD respond to +#value_to_der+.
         | 
| 183 178 | 
             
                  # @return [String] DER-formated string
         | 
| 184 179 | 
             
                  def to_der
         | 
| 185 | 
            -
                     | 
| 180 | 
            +
                    build
         | 
| 186 181 | 
             
                  end
         | 
| 187 182 |  | 
| 188 183 | 
             
                  # @return [::Boolean] +true+ if this is a primitive type
         | 
| @@ -192,7 +187,7 @@ module RASN1 | |
| 192 187 |  | 
| 193 188 | 
             
                  # @return [::Boolean] +true+ if this is a constructed type
         | 
| 194 189 | 
             
                  def constructed?
         | 
| 195 | 
            -
                     | 
| 190 | 
            +
                    (self.class < Constructed) || !!@constructed
         | 
| 196 191 | 
             
                  end
         | 
| 197 192 |  | 
| 198 193 | 
             
                  # Get ASN.1 type
         | 
| @@ -201,17 +196,10 @@ module RASN1 | |
| 201 196 | 
             
                    self.class.type
         | 
| 202 197 | 
             
                  end
         | 
| 203 198 |  | 
| 204 | 
            -
                  # Get  | 
| 199 | 
            +
                  # Get identifier value
         | 
| 205 200 | 
             
                  # @return [Integer]
         | 
| 206 | 
            -
                  def  | 
| 207 | 
            -
                     | 
| 208 | 
            -
                           self.class::ASN1_PC
         | 
| 209 | 
            -
                         elsif @constructed   # true
         | 
| 210 | 
            -
                           Constructed::ASN1_PC
         | 
| 211 | 
            -
                         else    # false
         | 
| 212 | 
            -
                           0
         | 
| 213 | 
            -
                         end
         | 
| 214 | 
            -
                    (@tag_value || self.class::TAG) | CLASSES[@asn1_class] | pc
         | 
| 201 | 
            +
                  def id
         | 
| 202 | 
            +
                    id_value
         | 
| 215 203 | 
             
                  end
         | 
| 216 204 |  | 
| 217 205 | 
             
                  # @abstract This method SHOULD be partly implemented by subclasses to parse
         | 
| @@ -222,11 +210,13 @@ module RASN1 | |
| 222 210 | 
             
                  # @return [Integer] total number of parsed bytes
         | 
| 223 211 | 
             
                  # @raise [ASN1Error] error on parsing
         | 
| 224 212 | 
             
                  def parse!(der, ber: false)
         | 
| 225 | 
            -
                    return 0 unless  | 
| 213 | 
            +
                    return 0 unless check_id(der)
         | 
| 226 214 |  | 
| 227 | 
            -
                     | 
| 215 | 
            +
                    id_size = Types.decode_identifier_octets(der).last
         | 
| 216 | 
            +
                    total_length, data = get_data(der[id_size..-1], ber)
         | 
| 217 | 
            +
                    total_length += id_size
         | 
| 228 218 | 
             
                    if explicit?
         | 
| 229 | 
            -
                      # Delegate to #explicit type to generate sub- | 
| 219 | 
            +
                      # Delegate to #explicit type to generate sub-value
         | 
| 230 220 | 
             
                      type = explicit_type
         | 
| 231 221 | 
             
                      type.value = @value
         | 
| 232 222 | 
             
                      type.parse!(data)
         | 
| @@ -247,11 +237,9 @@ module RASN1 | |
| 247 237 | 
             
                  # @param [Integer] level
         | 
| 248 238 | 
             
                  # @return [String]
         | 
| 249 239 | 
             
                  def inspect(level=0)
         | 
| 250 | 
            -
                    str =  | 
| 251 | 
            -
                    str <<  | 
| 252 | 
            -
                    str <<  | 
| 253 | 
            -
                    str << "#{type}: #{value.inspect}"
         | 
| 254 | 
            -
                    str << " OPTIONAL" if optional?
         | 
| 240 | 
            +
                    str = common_inspect(level)
         | 
| 241 | 
            +
                    str << " #{value.inspect}"
         | 
| 242 | 
            +
                    str << ' OPTIONAL' if optional?
         | 
| 255 243 | 
             
                    str << " DEFAULT #{@default}" unless @default.nil?
         | 
| 256 244 | 
             
                    str
         | 
| 257 245 | 
             
                  end
         | 
| @@ -265,6 +253,23 @@ module RASN1 | |
| 265 253 |  | 
| 266 254 | 
             
                  private
         | 
| 267 255 |  | 
| 256 | 
            +
                  def pc_bit
         | 
| 257 | 
            +
                    if @constructed.nil?
         | 
| 258 | 
            +
                      self.class::ASN1_PC
         | 
| 259 | 
            +
                    elsif @constructed # true
         | 
| 260 | 
            +
                      Constructed::ASN1_PC
         | 
| 261 | 
            +
                    else # false
         | 
| 262 | 
            +
                      Primitive::ASN1_PC
         | 
| 263 | 
            +
                    end
         | 
| 264 | 
            +
                  end
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                  def common_inspect(level)
         | 
| 267 | 
            +
                    lvl = level >= 0 ? level : 0
         | 
| 268 | 
            +
                    str = '  ' * lvl
         | 
| 269 | 
            +
                    str << "#{@name} " unless @name.nil?
         | 
| 270 | 
            +
                    str << "#{type}:"
         | 
| 271 | 
            +
                  end
         | 
| 272 | 
            +
             | 
| 268 273 | 
             
                  def value_to_der
         | 
| 269 274 | 
             
                    case @value
         | 
| 270 275 | 
             
                    when Base
         | 
| @@ -274,7 +279,7 @@ module RASN1 | |
| 274 279 | 
             
                    end
         | 
| 275 280 | 
             
                  end
         | 
| 276 281 |  | 
| 277 | 
            -
                  def der_to_value(der, ber:false)
         | 
| 282 | 
            +
                  def der_to_value(der, ber: false)
         | 
| 278 283 | 
             
                    @value = der
         | 
| 279 284 | 
             
                  end
         | 
| 280 285 |  | 
| @@ -285,6 +290,7 @@ module RASN1 | |
| 285 290 | 
             
                    set_tag options
         | 
| 286 291 | 
             
                    @value = options[:value]
         | 
| 287 292 | 
             
                    @name = options[:name]
         | 
| 293 | 
            +
                    @options = options
         | 
| 288 294 | 
             
                  end
         | 
| 289 295 |  | 
| 290 296 | 
             
                  def set_class(asn1_class)
         | 
| @@ -292,11 +298,9 @@ module RASN1 | |
| 292 298 | 
             
                    when nil
         | 
| 293 299 | 
             
                      @asn1_class = :universal
         | 
| 294 300 | 
             
                    when Symbol
         | 
| 295 | 
            -
                       | 
| 296 | 
            -
             | 
| 297 | 
            -
                       | 
| 298 | 
            -
                        raise ClassError
         | 
| 299 | 
            -
                      end
         | 
| 301 | 
            +
                      raise ClassError unless CLASSES.key? asn1_class
         | 
| 302 | 
            +
             | 
| 303 | 
            +
                      @asn1_class = asn1_class
         | 
| 300 304 | 
             
                    else
         | 
| 301 305 | 
             
                      raise ClassError
         | 
| 302 306 | 
             
                    end
         | 
| @@ -315,27 +319,27 @@ module RASN1 | |
| 315 319 | 
             
                  def set_tag(options)
         | 
| 316 320 | 
             
                    if options[:explicit]
         | 
| 317 321 | 
             
                      @tag = :explicit
         | 
| 318 | 
            -
                      @ | 
| 322 | 
            +
                      @id_value = options[:explicit]
         | 
| 319 323 | 
             
                      @constructed = options[:constructed]
         | 
| 320 324 | 
             
                    elsif options[:implicit]
         | 
| 321 325 | 
             
                      @tag = :implicit
         | 
| 322 | 
            -
                      @ | 
| 326 | 
            +
                      @id_value = options[:implicit]
         | 
| 323 327 | 
             
                      @constructed = options[:constructed]
         | 
| 324 328 | 
             
                    elsif options[:tag_value]
         | 
| 325 | 
            -
                      @ | 
| 329 | 
            +
                      @id_value = options[:tag_value]
         | 
| 326 330 | 
             
                      @constructed = options[:constructed]
         | 
| 327 331 | 
             
                    end
         | 
| 328 332 |  | 
| 329 | 
            -
                    @asn1_class = :context if @tag  | 
| 333 | 
            +
                    @asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
         | 
| 330 334 | 
             
                  end
         | 
| 331 335 |  | 
| 332 | 
            -
                  def  | 
| 333 | 
            -
                    !(!@default.nil?  | 
| 334 | 
            -
                      !(optional?  | 
| 336 | 
            +
                  def can_build?
         | 
| 337 | 
            +
                    !(!@default.nil? && (@value.nil? || (@value == @default))) &&
         | 
| 338 | 
            +
                      !(optional? && @value.nil?)
         | 
| 335 339 | 
             
                  end
         | 
| 336 340 |  | 
| 337 | 
            -
                  def  | 
| 338 | 
            -
                    if  | 
| 341 | 
            +
                  def build
         | 
| 342 | 
            +
                    if can_build?
         | 
| 339 343 | 
             
                      if explicit?
         | 
| 340 344 | 
             
                        v = explicit_type
         | 
| 341 345 | 
             
                        v.value = @value
         | 
| @@ -343,29 +347,52 @@ module RASN1 | |
| 343 347 | 
             
                      else
         | 
| 344 348 | 
             
                        encoded_value = value_to_der
         | 
| 345 349 | 
             
                      end
         | 
| 346 | 
            -
                       | 
| 350 | 
            +
                      encode_identifier_octets << encode_size(encoded_value.size) << encoded_value
         | 
| 347 351 | 
             
                    else
         | 
| 348 352 | 
             
                      ''
         | 
| 349 353 | 
             
                    end
         | 
| 350 354 | 
             
                  end
         | 
| 351 355 |  | 
| 352 | 
            -
                  def  | 
| 353 | 
            -
                     | 
| 354 | 
            -
             | 
| 356 | 
            +
                  def id_value
         | 
| 357 | 
            +
                    return @id_value if defined? @id_value
         | 
| 358 | 
            +
             | 
| 359 | 
            +
                    self.class::ID
         | 
| 360 | 
            +
                  end
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                  def encode_identifier_octets
         | 
| 363 | 
            +
                    id2octets.pack('C*')
         | 
| 364 | 
            +
                  end
         | 
| 365 | 
            +
             | 
| 366 | 
            +
                  def id2octets
         | 
| 367 | 
            +
                    first_octet = CLASSES[asn1_class] | pc_bit
         | 
| 368 | 
            +
                    if id < MULTI_OCTETS_ID
         | 
| 369 | 
            +
                      [first_octet | id]
         | 
| 355 370 | 
             
                    else
         | 
| 356 | 
            -
                       | 
| 371 | 
            +
                      [first_octet | MULTI_OCTETS_ID] + unsigned_to_chained_octets(id)
         | 
| 357 372 | 
             
                    end
         | 
| 358 373 | 
             
                  end
         | 
| 359 374 |  | 
| 375 | 
            +
                  # Encode an unsigned integer on multiple octets.
         | 
| 376 | 
            +
                  # Value is encoded on bit 6-0 of each octet, bit 7(MSB) indicates wether
         | 
| 377 | 
            +
                  # further octets follow.
         | 
| 378 | 
            +
                  def unsigned_to_chained_octets(value)
         | 
| 379 | 
            +
                    ary = []
         | 
| 380 | 
            +
                    while value.positive?
         | 
| 381 | 
            +
                      ary.unshift(value & 0x7f | 0x80)
         | 
| 382 | 
            +
                      value >>= 7
         | 
| 383 | 
            +
                    end
         | 
| 384 | 
            +
                    ary[-1] &= 0x7f
         | 
| 385 | 
            +
                    ary
         | 
| 386 | 
            +
                  end
         | 
| 387 | 
            +
             | 
| 360 388 | 
             
                  def encode_size(size)
         | 
| 361 389 | 
             
                    if size >= INDEFINITE_LENGTH
         | 
| 362 390 | 
             
                      bytes = []
         | 
| 363 391 | 
             
                      while size > 255
         | 
| 364 | 
            -
                        bytes | 
| 392 | 
            +
                        bytes.unshift(size & 0xff)
         | 
| 365 393 | 
             
                        size >>= 8
         | 
| 366 394 | 
             
                      end
         | 
| 367 | 
            -
                      bytes | 
| 368 | 
            -
                      bytes.reverse!
         | 
| 395 | 
            +
                      bytes.unshift(size)
         | 
| 369 396 | 
             
                      bytes.unshift(INDEFINITE_LENGTH | bytes.size)
         | 
| 370 397 | 
             
                      bytes.pack('C*')
         | 
| 371 398 | 
             
                    else
         | 
| @@ -373,15 +400,16 @@ module RASN1 | |
| 373 400 | 
             
                    end
         | 
| 374 401 | 
             
                  end
         | 
| 375 402 |  | 
| 376 | 
            -
                  def  | 
| 377 | 
            -
                     | 
| 378 | 
            -
                     | 
| 403 | 
            +
                  def check_id(der)
         | 
| 404 | 
            +
                    expected_id = encode_identifier_octets
         | 
| 405 | 
            +
                    real_id = der[0, expected_id.size]
         | 
| 406 | 
            +
                    if real_id != expected_id
         | 
| 379 407 | 
             
                      if optional?
         | 
| 380 408 | 
             
                        @value = nil
         | 
| 381 409 | 
             
                      elsif !@default.nil?
         | 
| 382 410 | 
             
                        @value = @default
         | 
| 383 411 | 
             
                      else
         | 
| 384 | 
            -
                         | 
| 412 | 
            +
                        raise_id_error(der)
         | 
| 385 413 | 
             
                      end
         | 
| 386 414 | 
             
                      false
         | 
| 387 415 | 
             
                    else
         | 
| @@ -390,33 +418,29 @@ module RASN1 | |
| 390 418 | 
             
                  end
         | 
| 391 419 |  | 
| 392 420 | 
             
                  def get_data(der, ber)
         | 
| 393 | 
            -
                    length = der[ | 
| 421 | 
            +
                    length = der[0, 1].unpack1('C')
         | 
| 394 422 | 
             
                    length_length = 0
         | 
| 395 423 |  | 
| 396 424 | 
             
                    if length == INDEFINITE_LENGTH
         | 
| 397 425 | 
             
                      if primitive?
         | 
| 398 | 
            -
                        raise ASN1Error, "malformed #{type} | 
| 399 | 
            -
                           | 
| 426 | 
            +
                        raise ASN1Error, "malformed #{type}: indefinite length " \
         | 
| 427 | 
            +
                          'forbidden for primitive types'
         | 
| 428 | 
            +
                      elsif ber
         | 
| 429 | 
            +
                        raise NotImplementedError, 'indefinite length not supported'
         | 
| 400 430 | 
             
                      else
         | 
| 401 | 
            -
                         | 
| 402 | 
            -
                          raise NotImplementedError, "TAG: indefinite length not " \
         | 
| 403 | 
            -
                            "supported yet"
         | 
| 404 | 
            -
                        else
         | 
| 405 | 
            -
                          raise ASN1Error, "TAG: indefinite length forbidden in DER " \
         | 
| 406 | 
            -
                            "encoding"
         | 
| 407 | 
            -
                        end
         | 
| 431 | 
            +
                        raise ASN1Error, 'indefinite length forbidden in DER encoding'
         | 
| 408 432 | 
             
                      end
         | 
| 409 433 | 
             
                    elsif length < INDEFINITE_LENGTH
         | 
| 410 | 
            -
                      data = der[ | 
| 434 | 
            +
                      data = der[1, length]
         | 
| 411 435 | 
             
                    else
         | 
| 412 436 | 
             
                      length_length = length & 0x7f
         | 
| 413 | 
            -
                      length = der[ | 
| 414 | 
            -
             | 
| 415 | 
            -
                      data = der[ | 
| 437 | 
            +
                      length = der[1, length_length].unpack('C*')
         | 
| 438 | 
            +
                                                    .reduce(0) { |len, b| (len << 8) | b }
         | 
| 439 | 
            +
                      data = der[1 + length_length, length]
         | 
| 416 440 | 
             
                    end
         | 
| 417 441 |  | 
| 418 | 
            -
                    total_length =  | 
| 419 | 
            -
                    total_length += length_length if length_length | 
| 442 | 
            +
                    total_length = 1 + length
         | 
| 443 | 
            +
                    total_length += length_length if length_length.positive?
         | 
| 420 444 |  | 
| 421 445 | 
             
                    [total_length, data]
         | 
| 422 446 | 
             
                  end
         | 
| @@ -425,31 +449,38 @@ module RASN1 | |
| 425 449 | 
             
                    self.class.new
         | 
| 426 450 | 
             
                  end
         | 
| 427 451 |  | 
| 428 | 
            -
                  def  | 
| 429 | 
            -
                    msg =  | 
| 452 | 
            +
                  def raise_id_error(der)
         | 
| 453 | 
            +
                    msg = name.nil? ? +'' : +"#{name}: "
         | 
| 454 | 
            +
                    msg << "Expected #{self2name} but get #{der2name(der)}"
         | 
| 430 455 | 
             
                    raise ASN1Error, msg
         | 
| 431 456 | 
             
                  end
         | 
| 432 457 |  | 
| 458 | 
            +
                  def class_from_numeric_id(id)
         | 
| 459 | 
            +
                    CLASSES.key(id & CLASS_MASK)
         | 
| 460 | 
            +
                  end
         | 
| 461 | 
            +
             | 
| 433 462 | 
             
                  def self2name
         | 
| 434 | 
            -
                    name =  | 
| 435 | 
            -
                    name << " #{tag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
         | 
| 463 | 
            +
                    name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
         | 
| 436 464 | 
             
                    if implicit? || explicit?
         | 
| 437 | 
            -
                      name << ' 0x% | 
| 465 | 
            +
                      name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
         | 
| 438 466 | 
             
                    else
         | 
| 439 467 | 
             
                      name << ' ' << self.class.type
         | 
| 440 468 | 
             
                    end
         | 
| 441 469 | 
             
                  end
         | 
| 442 470 |  | 
| 443 | 
            -
                  def  | 
| 444 | 
            -
                    return 'no  | 
| 471 | 
            +
                  def der2name(der)
         | 
| 472 | 
            +
                    return 'no ID' if der.nil? || der.empty?
         | 
| 473 | 
            +
             | 
| 474 | 
            +
                    asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
         | 
| 475 | 
            +
                    name = +"#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
         | 
| 476 | 
            +
                    type =  Types.constants.map { |c| Types.const_get(c) }
         | 
| 477 | 
            +
                                 .select { |klass| klass < Primitive || klass < Constructed }
         | 
| 478 | 
            +
                                 .find { |klass| klass::ID == id }
         | 
| 479 | 
            +
                    name << " #{type.nil? ? '0x%X (0x%s)' % [id, bin2hex(der[0...id_size])] : type.encoded_type}"
         | 
| 480 | 
            +
                  end
         | 
| 445 481 |  | 
| 446 | 
            -
             | 
| 447 | 
            -
                     | 
| 448 | 
            -
                    name << " #{itag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
         | 
| 449 | 
            -
                    type =  Types.constants.map { |c| Types.const_get(c) }.
         | 
| 450 | 
            -
                              select { |klass| klass < Primitive || klass < Constructed }.
         | 
| 451 | 
            -
                              find { |klass| klass::TAG == itag & 0x1f }
         | 
| 452 | 
            -
                    name << " #{type.nil? ? "0x%02X" % (itag & 0x1f) : type.encode_type }"
         | 
| 482 | 
            +
                  def bin2hex(str)
         | 
| 483 | 
            +
                    str.unpack1('H*')
         | 
| 453 484 | 
             
                  end
         | 
| 454 485 | 
             
                end
         | 
| 455 486 | 
             
              end
         |