Class: RASN1::Model Abstract

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

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

Parse a DER-encoded string

record = Record.parse(der_string)
record[:id]             # => RASN1::Types::Integer
record[:id].value       # => Integer
record[:id].to_i        # => Integer
record[:id].asn1_class  # => Symbol
record[:id].optional?   # => false
record[:id].default     # => nil
record[:room].optional  # => true
record[:house].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,
           content: [boolean(:rented, default: false),
                     model(:a_record, Record)])
end

Set values like this:

record2 = Record2.new
record2[:rented] = true
record2[:a_record][:id] = 65537
record2[:a_record][: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

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, define_type_accel, define_type_accel_base, define_type_accel_of, inherited, model, objectid, parse, root_options, wrapper

Constructor Details

#initialize(args = {}) ⇒ Model

Create a new instance of a RASN1::Model

Parameters:

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


388
389
390
391
392
393
# File 'lib/rasn1/model.rb', line 388

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)


522
523
524
525
526
527
528
# File 'lib/rasn1/model.rb', line 522

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:



384
385
386
# File 'lib/rasn1/model.rb', line 384

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



276
277
278
# File 'lib/rasn1/model.rb', line 276

%w[sequence set choice].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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



276
277
278
# File 'lib/rasn1/model.rb', line 276

%w[sequence set choice].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:



294
295
296
# File 'lib/rasn1/model.rb', line 294

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



276
277
278
# File 'lib/rasn1/model.rb', line 276

%w[sequence set choice].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:



294
295
296
# File 'lib/rasn1/model.rb', line 294

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



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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:



376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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


376
377
378
379
380
381
# File 'lib/rasn1/model.rb', line 376

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)


545
546
547
# File 'lib/rasn1/model.rb', line 545

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

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

Overloads:



403
404
405
406
407
408
409
410
411
412
413
# File 'lib/rasn1/model.rb', line 403

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:



419
420
421
422
423
424
# File 'lib/rasn1/model.rb', line 419

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



474
475
476
# File 'lib/rasn1/model.rb', line 474

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.



428
429
430
431
# File 'lib/rasn1/model.rb', line 428

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

#inspect(level = 0) ⇒ String

Parameters:

  • level (Integer) (defaults to: 0)

Returns:

  • (String)


538
539
540
# File 'lib/rasn1/model.rb', line 538

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

#keysArray<Symbol,String>

Get elements names

Returns:

  • (Array<Symbol,String>)


441
442
443
# File 'lib/rasn1/model.rb', line 441

def keys
  @elements.keys
end

#nameString

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

Returns:

  • (String)


435
436
437
# File 'lib/rasn1/model.rb', line 435

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:



467
468
469
# File 'lib/rasn1/model.rb', line 467

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

#respond_to_missing?(meth) ⇒ Boolean

Parameters:

  • meth (Symbol)

Returns:

  • (Boolean)


532
533
534
# File 'lib/rasn1/model.rb', line 532

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

#to_derString

Returns:

  • (String)


452
453
454
# File 'lib/rasn1/model.rb', line 452

def to_der
  root.to_der
end

#to_hHash

Return a hash image of model

Returns:

  • (Hash)


447
448
449
# File 'lib/rasn1/model.rb', line 447

def to_h
  private_to_h
end

#typeString

Give type name (aka class name)

Returns:

  • (String)


458
459
460
# File 'lib/rasn1/model.rb', line 458

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)


499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
# File 'lib/rasn1/model.rb', line 499

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