Class: RASN1::Model Abstract

Inherits:
Object
  • Object
show all
Extended by:
Accel
Defined in:
lib/rasn1/model.rb,
lib/rasn1/model/accel.rb

Overview

This class is abstract.

Model class is a base class to define ASN.1 models.

Create a simple ASN.1 model

Given this ASN.1 example: Record ::= SEQUENCE { id INTEGER, room [0] IMPLICIT INTEGER OPTIONAL, house [1] EXPLICIT INTEGER DEFAULT 0 }

You may create your model like this: class Record < RASN1::Model sequence(:record, content: [integer(:id), integer(:room, implicit: 0, optional: true), integer(:house, explicit: 1, default: 0)]) end

You may also use block style definition: class Record < RASN1::Model sequence :record do integer :id integer :room, implicit: 0, optional: true integer :house, explicit: 1, default: 0 end end

In a model, each element must have a unique name.

Parse a DER-encoded string

record = Record.parse(der_string) record # => RASN1::Types::Integer record.value # => Integer record.to_i # => Integer record.asn1_class # => Symbol record.optional? # => false record.default # => nil record.optional # => true record.default # => 0

You may also parse a BER-encoded string this way: record = Record.parse(der_string, ber: true)

Generate a DER-encoded string

record = Record.new(id: 12, room: 24) record.to_der

Create a more complex model

Models may be nested. For example: class Record2 < RASN1::Model sequence :record2 do boolean :rented, default: false) model :a_record, Record end end

Set values like this: record2 = Record2.new record2 = true record2[:id] = 65537 record2[:room] = 43 or like this: record2 = Record2.new(rented: true, a_record: { id: 65537, room: 43 })

Delegation

Model may delegate some methods to its root element. Thus, if root element is, for example, a Types::Choice, model may delegate #chosen and #chosen_value.

All methods defined by root may be delegated by model, unless model also defines this method.

Author:

  • Sylvain Daubert

  • adfoster-r7 ModelValidationError, track source location for dynamic class methods

Since:

  • 0.17 block notation

Defined Under Namespace

Modules: Accel Classes: BaseElem, ModelElem, WrapElem

Constant Summary collapse

SEQUENCE_TYPES =

Since:

  • 0.17 block notation

[Types::Sequence, Types::SequenceOf, Types::Set, Types::SetOf].freeze

Instance Attribute Summary collapse

Attributes included from Accel

#options

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Accel

any, define_type_accel, define_type_accel_base, define_type_accel_container, define_type_accel_of, elements, generate_content, inherited, model, objectid, parse, push_element, root=, root_options, wrapper

Constructor Details

#initialize(args = {}) ⇒ Model

Create a new instance of a RASN1::Model

Parameters:

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

Since:

  • 0.17 block notation



267
268
269
270
271
272
# File 'lib/rasn1/model.rb', line 267

def initialize(args={})
  @elements = {}
  generate_root(args)
  args.delete(:name)
  lazy_initialize(args) unless args.empty?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, **kwargs) ⇒ Object

Delegate some methods to root element

Parameters:

  • meth (Symbol)
  • args (Array)
  • kwargs (Hash)

Returns:

  • (Object)

Since:

  • 0.17 block notation



401
402
403
404
405
406
407
# File 'lib/rasn1/model.rb', line 401

def method_missing(meth, *args, **kwargs)
  if root.respond_to?(meth)
    root.send(meth, *args, **kwargs)
  else
    super
  end
end

Instance Attribute Details

#rootModel, ... (readonly)

Returns:

Since:

  • 0.17 block notation



263
264
265
# File 'lib/rasn1/model.rb', line 263

def root
  @root
end

Class Method Details

.bit_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.bmp_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.boolean(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.choice(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



155
156
157
# File 'lib/rasn1/model.rb', line 155

%w[sequence set choice].each do |type|
  self.define_type_accel_container(type, Types.const_get(type.capitalize))
end

.enumerated(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.ia5_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.integer(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.null(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.numeric_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.octet_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.printable_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.sequence(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



155
156
157
# File 'lib/rasn1/model.rb', line 155

%w[sequence set choice].each do |type|
  self.define_type_accel_container(type, Types.const_get(type.capitalize))
end

.sequence_of(name, type, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • type (Model, Types::Base)

    type for SEQUENCE OF

  • options (Hash)

Returns:

  • (Elem)

See Also:



173
174
175
# File 'lib/rasn1/model.rb', line 173

%w[sequence set].each do |type|
  define_type_accel_of(type, Types.const_get(:"#{type.capitalize}Of"))
end

.set(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



155
156
157
# File 'lib/rasn1/model.rb', line 155

%w[sequence set choice].each do |type|
  self.define_type_accel_container(type, Types.const_get(type.capitalize))
end

.set_of(name, type, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • type (Model, Types::Base)

    type for SET OF

  • options (Hash)

Returns:

  • (Elem)

See Also:



173
174
175
# File 'lib/rasn1/model.rb', line 173

%w[sequence set].each do |type|
  define_type_accel_of(type, Types.const_get(:"#{type.capitalize}Of"))
end

.universal_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.utf8_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:



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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

.visible_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

  • (Elem)

See Also:

  • Types::VisibleString#initialize


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

Types.primitives.each do |prim|
  next if prim == Types::ObjectId

  method_name = prim.type.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase.gsub(/\s+/, '_')
  self.define_type_accel_base(method_name, prim)
end

Instance Method Details

#==(other) ⇒ Boolean

Objects are equal if they have same class AND same DER

Parameters:

  • other (Base)

Returns:

  • (Boolean)

Since:

  • 0.17 block notation



424
425
426
# File 'lib/rasn1/model.rb', line 424

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

#[](name) ⇒ Model, ... #[](idx) ⇒ Model, ...

Overloads:

Since:

  • 0.17 block notation



282
283
284
285
286
287
288
289
290
291
292
# File 'lib/rasn1/model.rb', line 282

def [](name_or_idx)
  case name_or_idx
  when Symbol
    elt = @elements[name_or_idx]
    return elt unless elt.is_a?(Proc)

    @elements[name_or_idx] = elt.call
  when Integer
    root[name_or_idx]
  end
end

#[]=(name, value) ⇒ Object

Set value of element name. Element should be a Types::Base.

Parameters:

  • name (String, Symbol)
  • value (Object)

Returns:

  • (Object)

    value

Raises:

Since:

  • 0.17 block notation



298
299
300
301
302
303
# File 'lib/rasn1/model.rb', line 298

def []=(name, value)
  # Here, use #[] to force generation for lazy elements
  raise Error, 'cannot set value for a Model' if self[name].is_a?(Model)

  self[name].value = value
end

#do_parse(der, ber: false) ⇒ Object

See Also:

Since:

  • 0.17 block notation



353
354
355
# File 'lib/rasn1/model.rb', line 353

def do_parse(der, ber: false)
  root.do_parse(der, ber: ber)
end

#initialize_copyvoid

This method returns an undefined value.

clone @elements and initialize @root from this new @element.

Since:

  • 0.17 block notation



307
308
309
310
# File 'lib/rasn1/model.rb', line 307

def initialize_copy(*)
  @elements = @elements.clone
  @root = @elements[@root_name]
end

#inspect(level = 0) ⇒ String

Parameters:

  • level (Integer) (defaults to: 0)

Returns:

  • (String)

Since:

  • 0.17 block notation



417
418
419
# File 'lib/rasn1/model.rb', line 417

def inspect(level=0)
  "#{'  ' * level}(#{type}) #{root.inspect(-level)}"
end

#keysArray<Symbol,String>

Get elements names

Returns:

  • (Array<Symbol,String>)

Since:

  • 0.17 block notation



320
321
322
# File 'lib/rasn1/model.rb', line 320

def keys
  @elements.keys
end

#nameString

Give model name (a.k.a root name)

Returns:

  • (String)

Since:

  • 0.17 block notation



314
315
316
# File 'lib/rasn1/model.rb', line 314

def name
  @root_name
end

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

Parse a DER/BER encoded string, and modify object in-place.

Parameters:

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

    accept BER encoding or not

Returns:

  • (Integer)

    number of parsed bytes

Raises:

Since:

  • 0.17 block notation



346
347
348
# File 'lib/rasn1/model.rb', line 346

def parse!(der, ber: false)
  root.parse!(der, ber: ber)
end

#respond_to_missing?(meth) ⇒ Boolean

Parameters:

  • meth (Symbol)

Returns:

  • (Boolean)

Since:

  • 0.17 block notation



411
412
413
# File 'lib/rasn1/model.rb', line 411

def respond_to_missing?(meth, *)
  root.respond_to?(meth) || super
end

#to_derString

Returns:

  • (String)

Since:

  • 0.17 block notation



331
332
333
# File 'lib/rasn1/model.rb', line 331

def to_der
  root.to_der
end

#to_hHash

Return a hash image of model

Returns:

  • (Hash)

Since:

  • 0.17 block notation



326
327
328
# File 'lib/rasn1/model.rb', line 326

def to_h
  private_to_h
end

#typeString

Give type name (aka class name)

Returns:

  • (String)

Since:

  • 0.17 block notation



337
338
339
# File 'lib/rasn1/model.rb', line 337

def type
  self.class.type
end

#valueObject? #value(name, *args) ⇒ Object?

Examples:

class MyModel1 < RASN1::Model
  sequence('seq', content: [boolean('boolean'), integer('int')])
end
class MyModel2 < RASN1::Model
  sequence('root', content: [sequence_of('list', MyModel1)])
end
model = MyModel2.new
model.parse!(der)
# access to 2nd MyModel1.int in list
model.value('list', 1, 'int')

Overloads:

  • #valueObject?

    Get value of root element

    Returns:

    • (Object, nil)
  • #value(name, *args) ⇒ Object?

    Direct access to the value of name (nested) element of model.

    Parameters:

    • name (String, Symbol)
    • args (Array<Integer,String,Symbol>)

      more argument to access element. May be used to access content of a SequenceOf or a SetOf

    Returns:

    • (Object, nil)

Returns:

  • (Object, nil)

Since:

  • 0.17 block notation



378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/rasn1/model.rb', line 378

def value(name=nil, *args)
  if name.nil?
    root.value
  else
    elt = by_name(name)
    return nil if elt.nil?

    unless args.empty?
      args.each do |arg|
        elt = elt.root if elt.is_a?(Model)
        elt = elt[arg]
      end
    end

    elt.value
  end
end