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: Elem, WrapElem

Instance Attribute Summary

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: {})


371
372
373
374
375
# File 'lib/rasn1/model.rb', line 371

def initialize(args={})
  root = generate_root
  generate_elements(root)
  initialize_elements(self, args)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object

Delegate some methods to root element

Parameters:

  • meth (Symbol)


479
480
481
482
483
484
485
# File 'lib/rasn1/model.rb', line 479

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

Class Method Details

.bit_string(name, options) ⇒ Elem

Parameters:

  • name (Symbol, String)

    name of object in model

  • options (Hash)

Returns:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



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

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

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



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

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

See Also:



280
281
282
# File 'lib/rasn1/model.rb', line 280

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

See Also:



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

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

See Also:



280
281
282
# File 'lib/rasn1/model.rb', line 280

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

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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:

See Also:



362
363
364
365
366
367
# File 'lib/rasn1/model.rb', line 362

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)


500
501
502
# File 'lib/rasn1/model.rb', line 500

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

#[](name) ⇒ Types::Base

Give access to element name in model

Parameters:

  • name (String, Symbol)

Returns:



380
381
382
# File 'lib/rasn1/model.rb', line 380

def [](name)
  @elements[name]
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:



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

def []=(name, value)
  raise Error, 'cannot set value for a Model' if @elements[name].is_a? Model

  @elements[name].value = value
end

#inspect(level = 0) ⇒ String

Returns:

  • (String)


493
494
495
# File 'lib/rasn1/model.rb', line 493

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

#keysArray<Symbol,String>

Get elements names

Returns:

  • (Array<Symbol,String>)


402
403
404
# File 'lib/rasn1/model.rb', line 402

def keys
  @elements.keys
end

#nameString, Symbol

Get name from root type

Returns:

  • (String, Symbol)


396
397
398
# File 'lib/rasn1/model.rb', line 396

def name
  @root_name
end

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

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

Parameters:

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

    accept BER encoding or not

Returns:

  • (Integer)

    number of parsed bytes

Raises:



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

def parse!(str, ber: false)
  root.parse!(str.dup.force_encoding('BINARY'), ber: ber)
end

#respond_to_missing?(meth) ⇒ Boolean

Returns:

  • (Boolean)


488
489
490
# File 'lib/rasn1/model.rb', line 488

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

#rootTypes::Base, Model

Get root element from model

Returns:



414
415
416
# File 'lib/rasn1/model.rb', line 414

def root
  @elements[@root_name]
end

#to_derString

Returns:

  • (String)


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

def to_der
  root.to_der
end

#to_hHash

Return a hash image of model

Returns:

  • (Hash)


408
409
410
# File 'lib/rasn1/model.rb', line 408

def to_h
  private_to_h
end

#typeString

Give type name (aka class name)

Returns:

  • (String)


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

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)


459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
# File 'lib/rasn1/model.rb', line 459

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