Class: RASN2::Types::Base Abstract

Inherits:
Object
  • Object
show all
Includes:
Helpers::Colorize
Defined in:
lib/rasn2/types/base.rb,
lib/rasn2/tracer.rb

Overview

This class is abstract.

This is base class for all ASN.1 types.

Subclasses SHOULD define:

==Define an optional value An optional value may be defined using :optional key from #initialize:

Integer.new(:int, optional: true)

An optional value implies:

  • while parsing, if decoded ID is not optional expected ID, no ASN1Error is raised, and parser tries next field,
  • while encoding, if #value is nil, this value is not encoded. ==Define a default value A default value may be defined using :default key from #initialize: Integer.new(:int, default: 0) A default value implies:
  • while parsing, if decoded ID is not expected one, no ASN1Error is raised and parser sets default value to this ID. Then parser tries next field,
  • while encoding, if #value is equal to default value, this value is not encoded. ==Define a tagged value ASN.1 permits to define tagged values. By example: -- context specific tag CType ::= [0] EXPLICIT INTEGER -- application specific tag AType ::= [APPLICATION 1] EXPLICIT INTEGER -- private tag PType ::= [PRIVATE 2] EXPLICIT INTEGER These types may be defined as: ctype = RASN2::Types::Integer.new(explicit: 0) # with explicit, default #asn1_class is :context atype = RASN2::Types::Integer.new(explicit: 1, class: :application) ptype = RASN2::Types::Integer.new(explicit: 2, class: :private) Sometimes, an EXPLICIT type should be CONSTRUCTED. To do that, use :constructed option: ptype = RASN2::Types::Integer.new(explicit: 2, class: :private, constructed: true)

Implicit tagged values may also be defined: ctype_implicit = RASN2::Types::Integer.new(implicit: 0)

Author:

  • Sylvain Daubert

Constant Summary collapse

ID =

rubocop:disable Metrics/ClassLength

nil
CLASSES =

Allowed ASN.1 classes

{
  universal: 0x00,
  application: 0x40,
  context: 0x80,
  private: 0xc0
}.freeze
CLASS_MASK =

Binary mask to get class

0xc0
MULTI_OCTETS_ID =
0x1f
INDEFINITE_LENGTH =

Length value for indefinite length

0x80

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Helpers::Colorize

#begin_colorizer, #brace_surround, #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(options = {}) ⇒ Base

Returns a new instance of Base.

Parameters:

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

Options Hash (options):

  • :class (Symbol)

    ASN.1 class. Default value is :universal. If :explicit or :implicit: is defined, default value is :context.

  • :optional (::Boolean)

    define this tag as optional. Default is false

  • :default (Object)

    default value (ASN.1 DEFAULT)

  • :value (Object)

    value to set

  • :implicit (::Integer)

    define an IMPLICIT tagged type

  • :explicit (::Integer)

    define an EXPLICIT tagged type

  • :constructed (::Boolean)

    if true, set type as constructed. May only be used when :explicit is defined, else it is discarded.

  • :name (::String)

    name for this node



140
141
142
143
144
145
146
147
# File 'lib/rasn2/types/base.rb', line 140

def initialize(options={})
  @constructed = nil
  set_value(options.delete(:value))
  self.options = options
  specific_initializer
  @raw_data = ''.b
  @raw_length = ''.b
end

Instance Attribute Details

#asn1_classSymbol (readonly)

Returns:

  • (Symbol)


79
80
81
# File 'lib/rasn2/types/base.rb', line 79

def asn1_class
  @asn1_class
end

#defaultObject? (readonly)

Returns default value, if defined.

Returns:

  • (Object, nil)

    default value, if defined



81
82
83
# File 'lib/rasn2/types/base.rb', line 81

def default
  @default
end

#modelObject

Returns the value of attribute model.



50
51
52
# File 'lib/rasn2/types/base.rb', line 50

def model
  @model
end

#nameString? (readonly)

Returns:

  • (String, nil)


77
78
79
# File 'lib/rasn2/types/base.rb', line 77

def name
  @name
end

#optionsHash[Symbol, Object]

Returns:

  • (Hash[Symbol, Object])


83
84
85
# File 'lib/rasn2/types/base.rb', line 83

def options
  @options
end

Class Method Details

.constrained?Booleran

Say if a type is constrained. Always return false for predefined types

Returns:

  • (Booleran)


124
125
126
# File 'lib/rasn2/types/base.rb', line 124

def self.constrained?
  false
end

.encoded_typeString

Get ASN.1 type used to encode this one

Returns:

  • (String)


105
106
107
# File 'lib/rasn2/types/base.rb', line 105

def self.encoded_type
  type
end

.parse(der_or_ber, options = {}) ⇒ Base

Note:

More options are supported. See #initialize.

Parse a DER or BER string

Parameters:

  • der_or_ber (String)

    string to parse

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

Options Hash (options):

  • :ber (Boolean)

    if true, parse a BER string, else a DER one

Returns:



115
116
117
118
119
# File 'lib/rasn2/types/base.rb', line 115

def self.parse(der_or_ber, options={})
  obj = self.new(options)
  obj.parse!(der_or_ber, ber: options[:ber])
  obj
end

.start_tracingObject

Patch #do_parse to add tracing ability



95
96
97
98
99
100
# File 'lib/rasn2/tracer.rb', line 95

def start_tracing
  alias_method :do_parse_without_tracing, :do_parse
  alias_method :do_parse, :do_parse_with_tracing
  alias_method :do_parse_explicit_without_tracing, :do_parse_explicit
  alias_method :do_parse_explicit, :do_parse_explicit_with_tracing
end

.stop_tracingObject

Unpatch #do_parse to remove tracing ability



104
105
106
107
# File 'lib/rasn2/tracer.rb', line 104

def stop_tracing
  alias_method :do_parse, :do_parse_without_tracing
  alias_method :do_parse_explicit, :do_parse_explicit_without_tracing
end

.typeString

Get ASN.1 type

Returns:

  • (String)


97
98
99
100
101
# File 'lib/rasn2/types/base.rb', line 97

def self.type
  return @type if defined? @type

  @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
end

Instance Method Details

#==(other) ⇒ Boolean

Objects are equal if they have same class AND same DER

Parameters:

Returns:



307
308
309
# File 'lib/rasn2/types/base.rb', line 307

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

#asn1_class_to_sObject



416
417
418
# File 'lib/rasn2/types/base.rb', line 416

def asn1_class_to_s
  asn1_class.to_s.upcase
end

#attributesObject



265
266
267
268
269
270
271
272
# File 'lib/rasn2/types/base.rb', line 265

def attributes
  attributes = []
  attributes << 'OPTIONAL' if optional?
  attributes << 'CONSTRUCTED' if constructed? && !(self.class < Constructed)
  attributes << 'EXPLICIT' if explicit?
  attributes << 'IMPLICIT' if implicit?
  attributes
end

#bin2hex(str) ⇒ Object



708
709
710
# File 'lib/rasn2/types/base.rb', line 708

def bin2hex(str)
  str.unpack1('H*')
end

#buildObject



541
542
543
544
545
546
547
548
549
550
551
552
553
554
# File 'lib/rasn2/types/base.rb', line 541

def build
  if can_build?
    if explicit?
      v = explicit_type
      v.value = @value
      encoded_value = v.to_der
    else
      encoded_value = value_to_der
    end
    encode_identifier_octets << encode_size(encoded_value.size) << encoded_value
  else
    ''
  end
end

#can_build?Boolean

Say if DER can be built (not default value, not optional without value, has a value)

Returns:

Since:

  • 0.12.0



334
335
336
# File 'lib/rasn2/types/base.rb', line 334

def can_build?
  value? && (@default.nil? || (@value != @default))
end

#check_id(der) ⇒ Boolean

Check ID from der is the one expected

Returns:

Raises:

  • (ASN1Error)

    ID was not expected, is not optional and has no default value



607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
# File 'lib/rasn2/types/base.rb', line 607

def check_id(der) # rubocop:disable Naming/PredicateMethod
  expected_id = encode_identifier_octets
  real_id = der[0, expected_id.size]
  return true if real_id == expected_id

  if optional?
    @no_value = true
    @value = void_value
  elsif !@default.nil?
    @value = @default
  else
    return true if self.instance_of?(Tag)

    raise_id_error(der)
  end
  false
end

#colorize(msg) ⇒ Object



60
61
62
# File 'lib/rasn2/types/base.rb', line 60

def colorize(msg)
  tracer ? tracer.colorize(msg) : self.colorizer.wrap(msg)
end

#common_inspect(level = 0, color: false) ⇒ Object



443
444
445
446
447
448
449
450
451
452
453
454
# File 'lib/rasn2/types/base.rb', line 443

def common_inspect(level=0, color: false)
  begin_colorizer(color)
  parts = []
  parts << "(#{model})" if model
  parts << colorize_name(name) if name
  parts << colorize_id(id, asn1_class_to_s) if id && !universal?
  class_name = colorize_class(type, nil, *self.attributes)
  class_name += ':' if constructed?
  parts << class_name
  end_colorizer
  "#{"  " * level}" + parts.reject(&:empty?).join(' ').to_s
end

#constructed?::Boolean

Returns true if this is a constructed type.

Returns:

  • (::Boolean)

    true if this is a constructed type



223
224
225
# File 'lib/rasn2/types/base.rb', line 223

def constructed?
  (self.class < Constructed) || !!@constructed
end

#der2name(der) ⇒ Object



692
693
694
695
696
697
698
699
# File 'lib/rasn2/types/base.rb', line 692

def der2name(der)
  return 'no ID' if der.nil? || der.empty?

  asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
  name = "#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
  type =  find_type(id)
  name << " #{type.nil? ? bin2hex(der[0...id_size]).to_s : type.encoded_type}"
end

#der_to_value(der, ber: false) ⇒ void

This method returns an undefined value.

Make value from DER/BER string

Parameters:

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

Since:

  • 0.15.0 was private before



384
385
386
# File 'lib/rasn2/types/base.rb', line 384

def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
  @value = der
end

#do_parse(der, ber: false) ⇒ Array(::Integer, String)

Parameters:

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

Returns:

  • (Array(::Integer, String))

Since:

  • 0.15.0 was private before



358
359
360
361
362
363
364
365
366
367
# File 'lib/rasn2/types/base.rb', line 358

def do_parse(der, ber: false)
  return [0, ''] unless check_id(der)

  id_size = Types.decode_identifier_octets(der).last
  total_length, data = get_data(der[id_size..], ber)
  total_length += id_size
  @no_value = false

  [total_length, data]
end

#do_parse_explicit(data) ⇒ void

This method returns an undefined value.

Parameters:

  • data (String)

Since:

  • 0.15.0 was private before



373
374
375
376
377
# File 'lib/rasn2/types/base.rb', line 373

def do_parse_explicit(data)
  type = explicit_type
  type.parse!(data)
  @value = type.value
end

#do_parse_explicit_with_tracing(data) ⇒ Object



119
120
121
122
123
# File 'lib/rasn2/tracer.rb', line 119

def do_parse_explicit_with_tracing(data)
  tracer.tracing_level += 1
  do_parse_explicit_without_tracing(data)
  tracer.tracing_level -= 1
end

#do_parse_with_tracing(der, ber:) ⇒ Object

Parse der with tracing abillity

See Also:



113
114
115
116
117
# File 'lib/rasn2/tracer.rb', line 113

def do_parse_with_tracing(der, ber:)
  ret = do_parse_without_tracing(der, ber: ber)
  tracer.trace(self.trace)
  ret
end

#encode_identifier_octetsObject



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

def encode_identifier_octets
  id2octets.pack('C*')
end

#encode_size(size) ⇒ Object



588
589
590
591
592
593
594
595
596
597
598
599
600
601
# File 'lib/rasn2/types/base.rb', line 588

def encode_size(size)
  if size >= INDEFINITE_LENGTH
    bytes = []
    while size > 255
      bytes.unshift(size & 0xff)
      size >>= 8
    end
    bytes.unshift(size)
    bytes.unshift(INDEFINITE_LENGTH | bytes.size)
    bytes.pack('C*')
  else
    [size].pack('C')
  end
end

#explicit?::Boolean?

Say if a tagged type is explicit

Returns:

  • (::Boolean, nil)

    return nil if not tagged, return true if explicit, else false



195
196
197
# File 'lib/rasn2/types/base.rb', line 195

def explicit?
  defined?(@tag) ? @tag == :explicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#explicit_typeObject



666
667
668
# File 'lib/rasn2/types/base.rb', line 666

def explicit_type
  self.class.new(name: name)
end

#find_type(id) ⇒ Object



701
702
703
704
705
706
# File 'lib/rasn2/types/base.rb', line 701

def find_type(id)
  Types.constants.map { |c| Types.const_get(c) }
       .grep(Class)
       .select { |klass| klass < Primitive || klass < Constructed }
       .find { |klass| id == klass::ID }
end

#get_data(der, ber) ⇒ Object



625
626
627
628
629
630
631
632
633
634
635
636
637
638
# File 'lib/rasn2/types/base.rb', line 625

def get_data(der, ber)
  return [0, ''] if der.nil? || der.empty?

  length, length_length = get_length(der, ber)

  data = der[1 + length_length, length]
  @raw_length = der[0, length_length + 1]
  @raw_data = data

  total_length = 1 + length
  total_length += length_length if length_length.positive?

  [total_length, data]
end

#get_length(der, ber) ⇒ Object



640
641
642
643
644
645
646
647
648
649
650
651
652
653
# File 'lib/rasn2/types/base.rb', line 640

def get_length(der, ber)
  length = der.unpack1('C').to_i
  length_length = 0

  if length == INDEFINITE_LENGTH
    raise_on_indefinite_length(ber)
  elsif length > INDEFINITE_LENGTH
    length_length = length & 0x7f
    length = der[1, length_length].unpack('C*')
                                  .reduce(0) { |len, b| (len << 8) | b }
  end

  [length, length_length]
end

#idInteger

Get identifier value

Returns:



235
236
237
# File 'lib/rasn2/types/base.rb', line 235

def id
  id_value
end

#id2octetsObject



566
567
568
569
570
571
572
573
# File 'lib/rasn2/types/base.rb', line 566

def id2octets
  first_octet = CLASSES[asn1_class] | pc_bit
  if id < MULTI_OCTETS_ID
    [first_octet | id]
  else
    [first_octet | MULTI_OCTETS_ID] + unsigned_to_chained_octets(id)
  end
end

#id_valueObject



556
557
558
559
560
# File 'lib/rasn2/types/base.rb', line 556

def id_value
  return @id_value if defined?(@id_value) && !@id_value.nil?

  self.class.const_get(:ID)
end

#implicit?::Boolean?

Say if a tagged type is implicit

Returns:

  • (::Boolean, nil)

    return nil if not tagged, return true if implicit, else false



202
203
204
# File 'lib/rasn2/types/base.rb', line 202

def implicit?
  defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
end

#indent(input, by = 2) ⇒ Object



688
689
690
# File 'lib/rasn2/types/base.rb', line 688

def indent(input, by=2)
  input.split("\n").map { |line| ("  " * by) + line }.join("\n")
end

#initialize_copyObject

Deep copy @value and @default.



153
154
155
156
157
158
# File 'lib/rasn2/types/base.rb', line 153

def initialize_copy(*)
  super
  @value = @value.dup
  @no_value = @no_value.dup
  @default = @default.dup
end

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

Parameters:

Returns:

  • (String)


276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/rasn2/types/base.rb', line 276

def inspect(level=0, color: false)
  parts = []
  new_level = level.abs + 1

  begin_colorizer(color)
  parts << common_inspect(level, color: color)

  if constructed?
    if @value.is_a?(Array)
      @value.each do |value|
        parts << "\n"
        parts << value.inspect(new_level, color: color).to_s
      end
    else
      parts << "\n"
      if @value.is_a?(Base)
        parts << @value.inspect(new_level, color: color).to_s
      else
        @value.inspect.to_s
      end
    end
  else
    parts << inspect_value unless inspect_value.empty?
  end
  end_colorizer
  parts.reject(&:empty?).join(' ')
end

#inspect_valueObject



456
457
458
459
460
461
462
463
464
465
# File 'lib/rasn2/types/base.rb', line 456

def inspect_value
  str = if value?
          colorize_value(value.inspect)
        else
          colorize_nil('(NO VALUE)')
        end

  str += colorize_default(@default) if @default
  str
end

#msg_type(raw_id = nil, _encoded_id = nil, no_id: false) ⇒ Object



420
421
422
423
424
425
426
427
# File 'lib/rasn2/types/base.rb', line 420

def msg_type(raw_id=nil, _encoded_id=nil, no_id: false)
  parts = []
  parts << colorize_name(name) unless name.nil?
  real_id = no_id ? nil : raw_id || id
  parts << colorize_id(id, asn1_class_to_s) if real_id
  parts << colorize_class(type, real_id, *self.attributes)
  parts.reject(&:empty?).join(' ')
end

#optional?::Boolean

Say if this type is optional

Returns:

  • (::Boolean)


182
183
184
# File 'lib/rasn2/types/base.rb', line 182

def optional?
  @optional
end

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

This method is abstract.

This method SHOULD be partly implemented by subclasses to parse data. Subclasses SHOULD respond to #der_to_value.

Parse a DER string. This method updates object.

Parameters:

  • der (String)

    DER string

  • ber (Boolean) (defaults to: false)

    if true, accept BER encoding

Returns:

  • (Integer)

    total number of parsed bytes

Raises:



246
247
248
249
250
251
252
253
254
255
256
257
# File 'lib/rasn2/types/base.rb', line 246

def parse!(der, ber: false)
  total_length, data = do_parse(der, ber: ber)
  return 0 if total_length.zero?

  if explicit?
    do_parse_explicit(data)
  else
    der_to_value(data, ber: ber)
  end

  total_length
end

#pc_bitObject



429
430
431
432
433
434
435
436
437
# File 'lib/rasn2/types/base.rb', line 429

def pc_bit
  if @constructed.nil?
    self.class.const_get(:ASN1_PC)
  elsif @constructed # true
    Constructed::ASN1_PC
  else # false
    Primitive::ASN1_PC
  end
end

#primitive?::Boolean

Returns true if this is a primitive type.

Returns:

  • (::Boolean)

    true if this is a primitive type



218
219
220
# File 'lib/rasn2/types/base.rb', line 218

def primitive?
  (self.class < Primitive) && !@constructed
end

#private?Boolean

Returns:



206
207
208
# File 'lib/rasn2/types/base.rb', line 206

def private?
  self.instance_of?(Types::Tag)
end

#raise_id_error(der) ⇒ Object

Raises:



670
671
672
673
674
675
# File 'lib/rasn2/types/base.rb', line 670

def raise_id_error(der)
  msg = name.nil? ? +'' : "#{name}: "
  msg << "Expected #{self2name} but got #{der2name(der)}"

  raise ASN1Error, msg
end

#raise_on_indefinite_length(ber) ⇒ Object



655
656
657
658
659
660
661
662
663
664
# File 'lib/rasn2/types/base.rb', line 655

def raise_on_indefinite_length(ber)
  if primitive?
    raise ASN1Error, "malformed #{type}: indefinite length " \
                     'forbidden for primitive types'
  elsif ber
    raise NotImplementedError, 'indefinite length not supported'
  else
    raise ASN1Error, 'indefinite length forbidden in DER encoding'
  end
end

#self2nameObject



677
678
679
680
681
682
683
684
685
686
# File 'lib/rasn2/types/base.rb', line 677

def self2name
  parts = [asn1_class.to_s.upcase]
  parts << (constructed? ? 'CONSTRUCTED' : 'PRIMITIVE').to_s
  if implicit? || explicit?
    parts << '0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
  else
    parts << self.class.type.to_s
  end
  parts.reject(&:empty?).join(' ')
end

#set_class(asn1_class) ⇒ Object

rubocop:disable Naming/AccessorMethodName



476
477
478
479
480
481
482
483
484
485
486
487
# File 'lib/rasn2/types/base.rb', line 476

def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
  case asn1_class
  when nil
    @asn1_class = :universal
  when Symbol
    raise ClassError unless CLASSES.key?(asn1_class)

    @asn1_class = asn1_class
  else
    raise ClassError
  end
end

#set_default(default) ⇒ Object

rubocop:disable Naming/AccessorMethodName



493
494
495
# File 'lib/rasn2/types/base.rb', line 493

def set_default(default) # rubocop:disable Naming/AccessorMethodName
  @default = default
end

#set_optional(optional) ⇒ Object

rubocop:disable Naming/AccessorMethodName



489
490
491
# File 'lib/rasn2/types/base.rb', line 489

def set_optional(optional) # rubocop:disable Naming/AccessorMethodName
  @optional = !!optional
end

#set_tag(options) ⇒ Object

handle undocumented option :tag_value, used internally by RASN2.parse to parse non-universal class tags.



499
500
501
502
503
504
505
506
507
508
509
510
511
512
# File 'lib/rasn2/types/base.rb', line 499

def set_tag(options) # rubocop:disable Naming/AccessorMethodName
  @constructed = options[:constructed]
  if options[:explicit]
    @tag = :explicit
    @id_value = tag_id_to_integer(options[:explicit])
  elsif options[:implicit]
    @tag = :implicit
    @id_value = tag_id_to_integer(options[:implicit])
  elsif options[:tag_value]
    @id_value = tag_id_to_integer(options[:tag_value])
  end

  @asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
end

#set_value(value) ⇒ Object

rubocop:disable Naming/AccessorMethodName



530
531
532
533
534
535
536
537
538
539
# File 'lib/rasn2/types/base.rb', line 530

def set_value(value) # rubocop:disable Naming/AccessorMethodName
  if value.nil?
    @no_value = true
    @value = void_value
  else
    @no_value = false
    @value = value
  end
  value
end

#specific_initializerObject

This method is abstract.

To help subclass initialize itself. Default implementation do nothing.



150
# File 'lib/rasn2/types/base.rb', line 150

def specific_initializer; end

#tag_id_to_integer(value) ⇒ ::Integer

Convert a tag id to an integer. If given a String or Symbol, interprets the characters as big-endian octets (like Apple 4-character codes / 4CCs). Integers are returned as-is.

Parameters:

  • value (::Integer, String, Symbol)

    tag value

Returns:

  • (::Integer)


519
520
521
522
523
524
525
526
527
528
# File 'lib/rasn2/types/base.rb', line 519

def tag_id_to_integer(value)
  case value
  when ::Integer
    value
  when ::String, ::Symbol
    value.to_s.bytes.reduce(0) { |acc, b| (acc << 8) | b }
  else
    value
  end
end

#tagged?::Boolean

Say if this type is tagged or not

Returns:

  • (::Boolean)


188
189
190
# File 'lib/rasn2/types/base.rb', line 188

def tagged?
  !@tag.nil?
end

#to_derString

This method is abstract.

This method SHOULD be partly implemented by subclasses, which SHOULD respond to #value_to_der.

Returns DER-formated string.

Returns:

  • (String)

    DER-formated string



213
214
215
# File 'lib/rasn2/types/base.rb', line 213

def to_der
  build
end

#traceString

Returns:

  • (String)


340
341
342
343
344
345
346
347
348
349
350
351
# File 'lib/rasn2/types/base.rb', line 340

def trace
  return trace_real if value?

  parts = [msg_type]
  parts << if default.nil?
             colorize_nil
           else
             colorize_default(value)
           end

  parts.reject(&:empty?).join(' ')
end

#trace_data(data = nil) ⇒ Object



399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/rasn2/types/base.rb', line 399

def trace_data(data=nil)
  data ||= raw_data
  format_string = data.size > 65_535 ? '%08X' : '%04X'
  lines = data.bytes.each_slice(16).map.with_index do |chunk, index|
    offset = format_string % (index * 16)
    hex = chunk.map { |b| '%02x' % b }.join(' ')
    hex_padded = hex.ljust(47) # 16*2 hex digits + 15 spaces
    ascii = chunk.map { |b| (32..126).cover?(b) ? b.chr : '.' }.join
    "  #{offset}  #{hex_padded}  |#{ascii}|"
  end
  "\n#{lines.join("\n")}"
end

#trace_realObject



388
389
390
391
392
393
394
395
396
397
# File 'lib/rasn2/types/base.rb', line 388

def trace_real
  raw_id = encode_identifier_octets
  encoded_id = raw_id.unpack1('w*')
  data_length = raw_data.length
  encoded_length = raw_length.unpack1('w*')
  parts = [msg_type(raw_id.unpack1('H*').to_i(16), encoded_id)]
  msg = parts.reject(&:empty?).join(' ')
  msg << ", #{length_specifier(data_length, encoded_length)}"
  msg << trace_data
end

#tracerObject



91
92
93
# File 'lib/rasn2/types/base.rb', line 91

def tracer
  ::RASN2.tracer
end

#typeString

Get ASN.1 type

Returns:

  • (String)


229
230
231
# File 'lib/rasn2/types/base.rb', line 229

def type
  self.class.type
end

#universal?Boolean

Returns:



439
440
441
# File 'lib/rasn2/types/base.rb', line 439

def universal?
  @asn1_class == :universal
end

#unpack(binstr) ⇒ Object



412
413
414
# File 'lib/rasn2/types/base.rb', line 412

def unpack(binstr)
  binstr.unpack1('H*')
end

#unsigned_to_chained_octets(value) ⇒ Object

Encode an unsigned integer on multiple octets. Value is encoded on bit 6-0 of each octet, bit 7(MSB) indicates wether further octets follow.



578
579
580
581
582
583
584
585
586
# File 'lib/rasn2/types/base.rb', line 578

def unsigned_to_chained_octets(value)
  ary = []
  while value.positive?
    ary.unshift(value & 0x7f | 0x80)
    value >>= 7
  end
  ary[-1] &= 0x7f
  ary
end

#valueObject

Get value or default value



161
162
163
164
165
166
167
# File 'lib/rasn2/types/base.rb', line 161

def value
  if value?
    @value
  else
    @default
  end
end

#value=(val) ⇒ Object

Set value. If val is nil, unset value

Parameters:

  • val (Object, nil)


171
172
173
# File 'lib/rasn2/types/base.rb', line 171

def value=(val)
  set_value(val)
end

#value?Boolean

Say if a value is set

Returns:

Since:

  • 0.12.0



327
328
329
# File 'lib/rasn2/types/base.rb', line 327

def value?
  !@no_value
end

#value_sizeInteger

Give size in octets of encoded value

Returns:



261
262
263
# File 'lib/rasn2/types/base.rb', line 261

def value_size
  value_to_der.size
end

#value_to_derObject



467
468
469
470
471
472
473
474
# File 'lib/rasn2/types/base.rb', line 467

def value_to_der
  case @value
  when Base
    @value.to_der
  else
    @value.to_s
  end
end

#void_valueObject

This method is abstract.

Define 'void' value (i.e. 'value' when no value was set)



176
177
178
# File 'lib/rasn2/types/base.rb', line 176

def void_value
  ''
end