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


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

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)


499
500
501
502
503
504
505
# File 'lib/rasn1/model.rb', line 499

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:



374
375
376
# File 'lib/rasn1/model.rb', line 374

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



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

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



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



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

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



284
285
286
# File 'lib/rasn1/model.rb', line 284

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



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

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



284
285
286
# File 'lib/rasn1/model.rb', line 284

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



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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:



366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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


366
367
368
369
370
371
# File 'lib/rasn1/model.rb', line 366

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)


520
521
522
# File 'lib/rasn1/model.rb', line 520

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

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

Access an element of the model by its name

Parameters:

  • name (Symbol)

Returns:



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

def [](name)
  elt = @elements[name]
  return elt unless elt.is_a?(Proc)

  # Lazy element -> generate it
  @elements[name] = elt.call
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:



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

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



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

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.



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

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

#inspect(level = 0) ⇒ String

Returns:

  • (String)


513
514
515
# File 'lib/rasn1/model.rb', line 513

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

#keysArray<Symbol,String>

Get elements names

Returns:

  • (Array<Symbol,String>)


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

def keys
  @elements.keys
end

#nameString

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

Returns:

  • (String)


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

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:



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

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

#respond_to_missing?(meth) ⇒ Boolean

Returns:

  • (Boolean)


508
509
510
# File 'lib/rasn1/model.rb', line 508

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

#to_derString

Returns:

  • (String)


431
432
433
# File 'lib/rasn1/model.rb', line 431

def to_der
  root.to_der
end

#to_hHash

Return a hash image of model

Returns:

  • (Hash)


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

def to_h
  private_to_h
end

#typeString

Give type name (aka class name)

Returns:

  • (String)


437
438
439
# File 'lib/rasn1/model.rb', line 437

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)


477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
# File 'lib/rasn1/model.rb', line 477

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