Class: RASN2::Model Abstract

Inherits:
Object
  • Object
show all
Extended by:
Accel
Includes:
Helpers::Colorize
Defined in:
lib/rasn2/model.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 < RASN2::Model sequence(:record, content: [integer(:id), integer(:room, implicit: 0, optional: true), integer(:house, explicit: 1, default: 0)]) end

Since 0.17.0, content may also be defined through a block. This is strictly equivalent to the :content option: class Record < RASN2::Model sequence :record do integer :id integer :room, implicit: 0, optional: true integer :house, explicit: 1, default: 0 end end Blocks may be nested, and may use all model helpers (+#model+, #wrapper, #sequence_of, ...): class PersonnelRecord < RASN2::Model sequence :personnelRecord do utf8_string :name utf8_string :title integer :age boolean :employed end end When both :content option and a block are given, block elements are appended to those defined by the option.

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

Parse a DER-encoded string

record = Record.parse(der_string) record # => RASN2::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 < RASN2::Model sequence(:record2, content: [boolean(:rented, default: false), model(:a_record, Record)]) 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 }) Same model, using a block: class Record2 < RASN2::Model sequence :record2 do boolean :rented, default: false model :a_record, Record end end

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

Defined Under Namespace

Modules: Accel Classes: BaseElem, ModelElem, WrapElem

Constant Summary collapse

SEQUENCE_TYPES =
[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, capture_content, define_type_accel, define_type_accel_base, define_type_accel_of, inherited, model, objectid, parse, push_element, root_options, wrapper

Methods included from Helpers::Colorize

#begin_colorizer, #brace_surround, #colorize, #colorize_attribute, #colorize_bool, #colorize_class, #colorize_default, #colorize_enum, #colorize_id, #colorize_name, #colorize_nil, #colorize_value, #colorizer, #colorizer=, #end_colorizer, #int_with_hex, #length_specifier, #parens_hex

Constructor Details

#initialize(args = {}) ⇒ Model

Create a new instance of a RASN2::Model

Parameters:

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


485
486
487
488
489
# File 'lib/rasn2/model.rb', line 485

def initialize(args={})
  @elements = {}
  generate_root(args)
  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) ⇒ Hash

Return a hash image of model Delegate some methods to root element

Parameters:

  • meth (Symbol)

Returns:

  • (Hash)


626
627
628
629
630
631
632
# File 'lib/rasn2/model.rb', line 626

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:



481
482
483
# File 'lib/rasn2/model.rb', line 481

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



373
374
375
# File 'lib/rasn2/model.rb', line 373

%w[sequence set choice tag].each do |type|
  self.define_type_accel_base(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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



373
374
375
# File 'lib/rasn2/model.rb', line 373

%w[sequence set choice tag].each do |type|
  self.define_type_accel_base(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:



391
392
393
# File 'lib/rasn2/model.rb', line 391

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



373
374
375
# File 'lib/rasn2/model.rb', line 373

%w[sequence set choice tag].each do |type|
  self.define_type_accel_base(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:



391
392
393
# File 'lib/rasn2/model.rb', line 391

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



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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:



473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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


473
474
475
476
477
478
# File 'lib/rasn2/model.rb', line 473

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)


647
648
649
# File 'lib/rasn2/model.rb', line 647

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

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

Overloads:

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

    Access an element of the model by its name

    Parameters:

    • name (Symbol)

    Returns:

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

    Access an element of root element by its index. Root element must be a Sequence or SequenceOf.

    Parameters:

    • idx (Integer)

    Returns:



499
500
501
502
503
504
505
506
507
508
509
# File 'lib/rasn2/model.rb', line 499

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:



515
516
517
518
519
520
# File 'lib/rasn2/model.rb', line 515

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



579
580
581
# File 'lib/rasn2/model.rb', line 579

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

#initialize_copy(_other) ⇒ Object

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



523
524
525
526
# File 'lib/rasn2/model.rb', line 523

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

#inspect(level = 0, color: true) ⇒ String

Returns:

  • (String)


640
641
642
# File 'lib/rasn2/model.rb', line 640

def inspect(level=0, color: true)
  root.inspect(level, color: color, kind: type)
end

#keysArray<Symbol,String>

Get elements names

Returns:

  • (Array<Symbol,String>)


536
537
538
# File 'lib/rasn2/model.rb', line 536

def keys
  @elements.keys
end

#nameString

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

Returns:

  • (String)


530
531
532
# File 'lib/rasn2/model.rb', line 530

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:



571
572
573
574
575
# File 'lib/rasn2/model.rb', line 571

def parse!(der, ber: false)
  result = root.parse!(der, ber: ber)
  result.model = self if result.respond_to? :model=
  result
end

#respond_to_missing?(meth) ⇒ Boolean

Returns:

  • (Boolean)


635
636
637
# File 'lib/rasn2/model.rb', line 635

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

#to_asn1(name: nil, type_name: nil) ⇒ String

Generate ASN.1 value notation text from this model instance.

Parameters:

  • name (String) (defaults to: nil)

    the value name (e.g. 'myValue')

  • type_name (String) (defaults to: nil)

    the ASN.1 type name (e.g. 'PersonnelRecord')

Returns:

  • (String)

    value notation text

Since:

  • 0.17.0



556
557
558
# File 'lib/rasn2/model.rb', line 556

def to_asn1(name: nil, type_name: nil)
  ValueNotation.emit(self, name: name, type_name: type_name)
end

#to_derString

Returns:

  • (String)


547
548
549
# File 'lib/rasn2/model.rb', line 547

def to_der
  root.to_der
end

#to_hHash

Return a hash image of model

Returns:

  • (Hash)


542
543
544
# File 'lib/rasn2/model.rb', line 542

def to_h
  private_to_h
end

#typeString

Give type name (aka class name)

Returns:

  • (String)


562
563
564
# File 'lib/rasn2/model.rb', line 562

def type
  self.class.type
end

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

Examples:

class MyModel1 < RASN2::Model
  sequence('seq', content: [boolean('boolean'), integer('int')])
end
class MyModel2 < RASN2::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)


604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
# File 'lib/rasn2/model.rb', line 604

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