Class: RASN1::Types::Base Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/rasn1/types/base.rb

Overview

This class is abstract.

This is base class for all ASN.1 types.

Subclasses SHOULD define:

  • a TAG constant defining ASN.1 tag number,

  • a private method #value_to_der converting its #value to DER,

  • a private method #der_to_value converting DER into #value.

Define an optional value

An optional value may be defined using :optional key from #initialize:

Integer.new(:int, optional: true)

An optional value implies:

  • while parsing, if decoded tag is not optional expected tag, no ASN1Error is raised, and parser tries net tag,

  • while encoding, if #value is nil, this value is not encoded.

Define a default value

A default value may be defined using :default key from #initialize:

Integer.new(:int, default: 0)

A default value implies:

  • while parsing, if decoded tag is not expected tag, no ASN1Error is raised and parser sets default value to this tag. Then parser tries nex tag,

  • while encoding, if #value is equal to default value, this value is not encoded.

Define a tagged value

ASN.1 permits to define tagged values. By example:

-- context specific tag
CType ::= [0] EXPLICIT INTEGER
-- application specific tag
AType ::= [APPLICATION 1] EXPLICIT INTEGER
-- private tag
PType ::= [PRIVATE 2] EXPLICIT INTEGER

These types may be defined as:

ctype = RASN1::Types::Integer.new(explicit: 0)                      # with explicit, default #asn1_class is :context
atype = RASN1::Types::Integer.new(explicit: 1, class: :application)
ptype = RASN1::Types::Integer.new(explicit: 2, class: :private)

Sometimes, an EXPLICIT type should be CONSTRUCTED. To do that, use :constructed option:

ptype = RASN1::Types::Integer.new(explicit: 2, class: :private, constructed: true)

Implicit tagged values may also be defined:

ctype_implicit = RASN1::Types::Integer.new(implicit: 0)

Author:

  • Sylvain Daubert

Direct Known Subclasses

Any, Choice, Constructed, Primitive

Constant Summary collapse

CLASSES =

Allowed ASN.1 tag classes

{
 universal:   0x00,
 application: 0x40,
 context:     0x80,
 private:     0xc0
}
MAX_TAG =

Maximum ASN.1 tag number

0x1e
INDEFINITE_LENGTH =

Length value for indefinite length

0x80

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ Base #initialize(value, options = {}) ⇒ Base

Returns a new instance of Base.

Overloads:

  • #initialize(options = {}) ⇒ Base

    Parameters:

    • options (Hash) (defaults to: {})

    Options Hash (options):

    • :class (Symbol)

      ASN.1 tag class. Default value is :universal. If :explicit or :implicit: is defined, default value is :context.

    • :optional (::Boolean)

      define this tag as optional. Default is false

    • :default (Object)

      default value for DEFAULT tag

    • :value (Object)

      value to set

    • :implicit (::Integer)

      define an IMPLICIT tagged type

    • :explicit (::Integer)

      define an EXPLICIT tagged type

    • :constructed (::Boolean)

      if true, set type as constructed. May only be used when :explicit is defined, else it is discarded.

    • :name (::String)

      name for this node

  • #initialize(value, options = {}) ⇒ Base

    Parameters:

    • value (Object)

      value to set for this ASN.1 object

    • options (Hash) (defaults to: {})

    Options Hash (options):

    • :class (Symbol)

      ASN.1 tag class. Default value is :universal. If :explicit or :implicit: is defined, default value is :context.

    • :optional (::Boolean)

      define this tag as optional. Default is false

    • :default (Object)

      default value for DEFAULT tag

    • :implicit (::Integer)

      define an IMPLICIT tagged type

    • :explicit (::Integer)

      define an EXPLICIT tagged type

    • :constructed (::Boolean)

      if true, set type as constructed. May only be used when :explicit is defined, else it is discarded.

    • :name (::String)

      name for this node



115
116
117
118
119
120
121
122
123
# File 'lib/rasn1/types/base.rb', line 115

def initialize(value_or_options={}, options={})
  @constructed = nil
  if value_or_options.is_a? Hash
    set_options value_or_options
  else
    set_options options
    @value = value_or_options
  end
end

Instance Attribute Details

#asn1_classSymbol (readonly)

Returns:

  • (Symbol)


64
65
66
# File 'lib/rasn1/types/base.rb', line 64

def asn1_class
  @asn1_class
end

#defaultObject? (readonly)

Returns default value, if defined.

Returns:

  • (Object, nil)

    default value, if defined



66
67
68
# File 'lib/rasn1/types/base.rb', line 66

def default
  @default
end

#nameString? (readonly)

Returns:

  • (String, nil)


62
63
64
# File 'lib/rasn1/types/base.rb', line 62

def name
  @name
end

#valueObject

Get value or default value



142
143
144
145
146
147
148
# File 'lib/rasn1/types/base.rb', line 142

def value
  if @value.nil?
    @default
  else
    @value
  end
end

Class Method Details

.parse(der_or_ber, options = {}) ⇒ Object

Note:

More options are supported. See #initialize.

Parse a DER or BER string

Parameters:

  • der_or_ber (String)

    string to parse

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :ber (Boolean)

    if true, parse a BER string, else a DER one



82
83
84
85
86
# File 'lib/rasn1/types/base.rb', line 82

def self.parse(der_or_ber, options={})
  obj = self.new(options)
  obj.parse!(der_or_ber, ber: options[:ber])
  obj
end

.typeString

Get ASN.1 type

Returns:

  • (String)


72
73
74
75
# File 'lib/rasn1/types/base.rb', line 72

def self.type
  return @type if @type
  @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
end

Instance Method Details

#==(other) ⇒ Boolean

Objects are equal if they have same class AND same DER

Parameters:

Returns:



262
263
264
# File 'lib/rasn1/types/base.rb', line 262

def ==(other)
  (other.class == self.class) && (other.to_der == self.to_der)
end

#constructed?::Boolean

Returns true if this is a constructed type.

Returns:

  • (::Boolean)

    true if this is a constructed type



188
189
190
# File 'lib/rasn1/types/base.rb', line 188

def constructed?
  !!((self.class < Constructed) ||  @constructed)
end

#explicit?::Boolean?

Say if a tagged type is explicit

Returns:

  • (::Boolean, nil)

    return nil if not tagged, return true if explicit, else false



164
165
166
# File 'lib/rasn1/types/base.rb', line 164

def explicit?
  @tag.nil? ? @tag : @tag == :explicit
end

#implicit?::Boolean?

Say if a tagged type is implicit

Returns:

  • (::Boolean, nil)

    return nil if not tagged, return true if implicit, else false



171
172
173
# File 'lib/rasn1/types/base.rb', line 171

def implicit?
  @tag.nil? ? @tag : @tag == :implicit
end

#initialize_copy(other) ⇒ Object

Used by #dup and #clone. Deep copy @value.



126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/rasn1/types/base.rb', line 126

def initialize_copy(other)
  @value = case
           when NilClass, TrueClass, FalseClass, Integer
             @value
           else
             @value.dup
           end
  @default = case
           when NilClass, TrueClass, FalseClass, Integer
             @default
           else
             @default.dup
           end
end

#inspect(level = 0) ⇒ Object



240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/rasn1/types/base.rb', line 240

def inspect(level=0)
  str = ''
  str << '  ' * level if level > 0
  str << "#{@name} " unless @name.nil?
  if self.class == Base
    str << "#{type} (0x#{'%02x' % tag}): "
    if @value.is_a?(Base)
      str << value.inspect(level+1)
    else
      str << value.inspect
    end
  else
    str << "#{type}: #{value.inspect}"
    str << " OPTIONAL" if optional?
    str << " DEFAULT #{@default}" unless @default.nil?
    str
  end
end

#optional?::Boolean

Returns:

  • (::Boolean)


151
152
153
# File 'lib/rasn1/types/base.rb', line 151

def optional?
  @optional
end

#parse!(der, ber: false) ⇒ Integer

This method is abstract.

This method SHOULD be partly implemented by subclasses to parse data. Subclasses SHOULD respond to #der_to_value.

Parse a DER string. This method updates object.

Parameters:

  • der (String)

    DER string

  • ber (Boolean) (defaults to: false)

    if true, accept BER encoding

Returns:

  • (Integer)

    total number of parsed bytes

Raises:



218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
# File 'lib/rasn1/types/base.rb', line 218

def parse!(der, ber: false)
  return 0 unless check_tag(der)

  total_length, data = get_data(der, ber)
  if explicit?
    # Delegate to #explicit type to generate sub-tag
    type = explicit_type
    type.parse!(data)
    @value = type.value
  else
    der_to_value(data, ber: ber)
  end

  total_length
end

#primitive?::Boolean

Returns true if this is a primitive type.

Returns:

  • (::Boolean)

    true if this is a primitive type



183
184
185
# File 'lib/rasn1/types/base.rb', line 183

def primitive?
  (self.class < Primitive) && !@constructed
end

#tagInteger

Get tag value

Returns:



200
201
202
203
204
205
206
207
208
209
# File 'lib/rasn1/types/base.rb', line 200

def tag
  pc = if @constructed.nil?
         self.class::ASN1_PC
       elsif @constructed   # true
         Constructed::ASN1_PC
       else    # false
         0
       end
  (@tag_value || self.class::TAG) | CLASSES[@asn1_class] | pc
end

#tagged?::Boolean

Say if this type is tagged or not

Returns:

  • (::Boolean)


157
158
159
# File 'lib/rasn1/types/base.rb', line 157

def tagged?
  !@tag.nil?
end

#to_derString

This method is abstract.

This method SHOULD be partly implemented by subclasses, which SHOULD respond to #value_to_der.

Returns DER-formated string.

Returns:

  • (String)

    DER-formated string



178
179
180
# File 'lib/rasn1/types/base.rb', line 178

def to_der
  build_tag
end

#typeString

Get ASN.1 type

Returns:

  • (String)


194
195
196
# File 'lib/rasn1/types/base.rb', line 194

def type
  self.class.type
end

#value_sizeInteger

Give size in octets of encoded value

Returns:



236
237
238
# File 'lib/rasn1/types/base.rb', line 236

def value_size
  value_to_der.size
end