Class: RASN1::Types::Base Abstract

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

Overview

This class is abstract.

This is base class for all ASN.1 types.

Subclasses SHOULD define:

  • an ID constant defining ASN.1 BER/DER identification number,

  • a method #der_to_value converting DER into #value.

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

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 ID is not optional expected ID, no ASN1Error is raised, and parser tries next field,

  • 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 ID is not expected one, no ASN1Error is raised and parser sets default value to this ID. Then parser tries next field,

  • 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

Constant Summary collapse

CLASSES =

Allowed ASN.1 classes

{
  universal: 0x00,
  application: 0x40,
  context: 0x80,
  private: 0xc0
}.freeze
CLASS_MASK =

Binary mask to get class

0xc0
MULTI_OCTETS_ID =
0x1f
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

Returns a new instance of Base.

Parameters:

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

Options Hash (options):

  • :class (Symbol)

    ASN.1 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 (ASN.1 DEFAULT)

  • :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



132
133
134
135
136
137
138
139
# File 'lib/rasn1/types/base.rb', line 132

def initialize(options={})
  @constructed = nil
  set_value(options.delete(:value))
  self.options = options
  specific_initializer
  @raw_data = ''.b
  @raw_length = ''.b
end

Instance Attribute Details

#asn1_classSymbol (readonly)

Returns:

  • (Symbol)


75
76
77
# File 'lib/rasn1/types/base.rb', line 75

def asn1_class
  @asn1_class
end

#defaultObject? (readonly)

Returns default value, if defined.

Returns:

  • (Object, nil)

    default value, if defined



77
78
79
# File 'lib/rasn1/types/base.rb', line 77

def default
  @default
end

#nameString? (readonly)

Returns:

  • (String, nil)


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

def name
  @name
end

#optionsHash[Symbol, Object]

Returns:

  • (Hash[Symbol, Object])


79
80
81
# File 'lib/rasn1/types/base.rb', line 79

def options
  @options
end

Class Method Details

.constrained?Booleran

Say if a type is constrained. Always return false for predefined types

Returns:

  • (Booleran)


116
117
118
# File 'lib/rasn1/types/base.rb', line 116

def self.constrained?
  false
end

.encoded_typeString

Get ASN.1 type used to encode this one

Returns:

  • (String)


97
98
99
# File 'lib/rasn1/types/base.rb', line 97

def self.encoded_type
  type
end

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

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

Returns:



107
108
109
110
111
# File 'lib/rasn1/types/base.rb', line 107

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

.start_tracingObject

Patch #do_parse to add tracing ability



74
75
76
77
78
79
# File 'lib/rasn1/tracer.rb', line 74

def start_tracing
  alias_method :do_parse_without_tracing, :do_parse
  alias_method :do_parse, :do_parse_with_tracing
  alias_method :do_parse_explicit_without_tracing, :do_parse_explicit
  alias_method :do_parse_explicit, :do_parse_explicit_with_tracing
end

.stop_tracingObject

Unpatch #do_parse to remove tracing ability



83
84
85
86
# File 'lib/rasn1/tracer.rb', line 83

def stop_tracing
  alias_method :do_parse, :do_parse_without_tracing
  alias_method :do_parse_explicit, :do_parse_explicit_without_tracing
end

.typeString

Get ASN.1 type

Returns:

  • (String)


89
90
91
92
93
# File 'lib/rasn1/types/base.rb', line 89

def self.type
  return @type if defined? @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:



266
267
268
# File 'lib/rasn1/types/base.rb', line 266

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

#can_build?Boolean

Say if DER can be built (not default value, not optional without value, has a value)

Returns:

Since:

  • 0.12.0



293
294
295
# File 'lib/rasn1/types/base.rb', line 293

def can_build?
  value? && (@default.nil? || (@value != @default))
end

#constructed?::Boolean

Returns true if this is a constructed type.

Returns:

  • (::Boolean)

    true if this is a constructed type



211
212
213
# File 'lib/rasn1/types/base.rb', line 211

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

#der_to_value(der, ber: false) ⇒ void

This method returns an undefined value.

Make value from DER/BER string

Parameters:

  • der (String)
  • ber (::Boolean) (defaults to: false)

Since:

  • 0.15.0 was private before



341
342
343
# File 'lib/rasn1/types/base.rb', line 341

def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
  @value = der
end

#do_parse(der, ber: false) ⇒ Array(::Integer, String)

Parameters:

  • der (String)
  • ber (Boolean) (defaults to: false)

Returns:

  • (Array(::Integer, String))

Since:

  • 0.15.0 was private before



315
316
317
318
319
320
321
322
323
324
# File 'lib/rasn1/types/base.rb', line 315

def do_parse(der, ber: false)
  return [0, ''] unless check_id(der)

  id_size = Types.decode_identifier_octets(der).last
  total_length, data = get_data(der[id_size..], ber)
  total_length += id_size
  @no_value = false

  [total_length, data]
end

#do_parse_explicit(data) ⇒ void

This method returns an undefined value.

Parameters:

  • data (String)

Since:

  • 0.15.0 was private before



330
331
332
333
334
# File 'lib/rasn1/types/base.rb', line 330

def do_parse_explicit(data)
  type = explicit_type
  type.parse!(data)
  @value = type.value
end

#do_parse_explicit_with_tracing(data) ⇒ Object



98
99
100
101
102
# File 'lib/rasn1/tracer.rb', line 98

def do_parse_explicit_with_tracing(data)
  RASN1.tracer.tracing_level += 1
  do_parse_explicit_without_tracing(data)
  RASN1.tracer.tracing_level -= 1
end

#do_parse_with_tracing(der, ber:) ⇒ Object

Parse der with tracing abillity

See Also:



92
93
94
95
96
# File 'lib/rasn1/tracer.rb', line 92

def do_parse_with_tracing(der, ber:)
  ret = do_parse_without_tracing(der, ber: ber)
  RASN1.tracer.trace(self.trace)
  ret
end

#explicit?::Boolean?

Say if a tagged type is explicit

Returns:

  • (::Boolean, nil)

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



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

def explicit?
  defined?(@tag) ? @tag == :explicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#idInteger

Get identifier value

Returns:



223
224
225
# File 'lib/rasn1/types/base.rb', line 223

def id
  id_value
end

#implicit?::Boolean?

Say if a tagged type is implicit

Returns:

  • (::Boolean, nil)

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



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

def implicit?
  defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#initialize_copyObject

Deep copy @value and @default.



145
146
147
148
149
150
# File 'lib/rasn1/types/base.rb', line 145

def initialize_copy(*)
  super
  @value = @value.dup
  @no_value = @no_value.dup
  @default = @default.dup
end

#inspect(level = 0) ⇒ String

Parameters:

Returns:

  • (String)


255
256
257
258
259
260
261
# File 'lib/rasn1/types/base.rb', line 255

def inspect(level=0)
  str = common_inspect(level)
  str << ' ' << inspect_value
  str << ' OPTIONAL' if optional?
  str << " DEFAULT #{@default}" unless @default.nil?
  str
end

#optional?::Boolean

Say if this type is optional

Returns:

  • (::Boolean)


174
175
176
# File 'lib/rasn1/types/base.rb', line 174

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:



234
235
236
237
238
239
240
241
242
243
244
245
# File 'lib/rasn1/types/base.rb', line 234

def parse!(der, ber: false)
  total_length, data = do_parse(der, ber: ber)
  return 0 if total_length.zero?

  if explicit?
    do_parse_explicit(data)
  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



206
207
208
# File 'lib/rasn1/types/base.rb', line 206

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

#specific_initializerObject

This method is abstract.

To help subclass initialize itself. Default implementation do nothing.



142
# File 'lib/rasn1/types/base.rb', line 142

def specific_initializer; end

#tagged?::Boolean

Say if this type is tagged or not

Returns:

  • (::Boolean)


180
181
182
# File 'lib/rasn1/types/base.rb', line 180

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



201
202
203
# File 'lib/rasn1/types/base.rb', line 201

def to_der
  build
end

#traceString

Returns:

  • (String)


299
300
301
302
303
304
305
306
307
308
# File 'lib/rasn1/types/base.rb', line 299

def trace
  return trace_real if value?

  msg = msg_type
  if default.nil? # rubocop:disable Style/ConditionalAssignment
    msg << ' NONE'
  else
    msg << " DEFAULT VALUE #{default}"
  end
end

#typeString

Get ASN.1 type

Returns:

  • (String)


217
218
219
# File 'lib/rasn1/types/base.rb', line 217

def type
  self.class.type
end

#valueObject

Get value or default value



153
154
155
156
157
158
159
# File 'lib/rasn1/types/base.rb', line 153

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

#value=(val) ⇒ Object

Set value. If val is nil, unset value

Parameters:

  • val (Object, nil)


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

def value=(val)
  set_value(val)
end

#value?Boolean

Say if a value is set

Returns:

Since:

  • 0.12.0



286
287
288
# File 'lib/rasn1/types/base.rb', line 286

def value?
  !@no_value
end

#value_sizeInteger

Give size in octets of encoded value

Returns:



249
250
251
# File 'lib/rasn1/types/base.rb', line 249

def value_size
  value_to_der.size
end

#void_valueObject

This method is abstract.

Define ‘void’ value (i.e. ‘value’ when no value was set)



168
169
170
# File 'lib/rasn1/types/base.rb', line 168

def void_value
  ''
end