rasn2 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +4 -0
  4. data/lib/rasn1/errors.rb +55 -0
  5. data/lib/rasn1/helpers/colorize.rb +76 -0
  6. data/lib/rasn1/model.rb +831 -0
  7. data/lib/rasn1/schema_parser.rb +470 -0
  8. data/lib/rasn1/tracer.rb +200 -0
  9. data/lib/rasn1/types/any.rb +98 -0
  10. data/lib/rasn1/types/base.rb +675 -0
  11. data/lib/rasn1/types/bit_string.rb +100 -0
  12. data/lib/rasn1/types/bmp_string.rb +22 -0
  13. data/lib/rasn1/types/boolean.rb +57 -0
  14. data/lib/rasn1/types/choice.rb +158 -0
  15. data/lib/rasn1/types/constrained.rb +51 -0
  16. data/lib/rasn1/types/constructed.rb +51 -0
  17. data/lib/rasn1/types/enumerated.rb +44 -0
  18. data/lib/rasn1/types/generalized_time.rb +156 -0
  19. data/lib/rasn1/types/ia5_string.rb +21 -0
  20. data/lib/rasn1/types/integer.rb +166 -0
  21. data/lib/rasn1/types/null.rb +43 -0
  22. data/lib/rasn1/types/numeric_string.rb +41 -0
  23. data/lib/rasn1/types/object_id.rb +64 -0
  24. data/lib/rasn1/types/octet_string.rb +52 -0
  25. data/lib/rasn1/types/primitive.rb +13 -0
  26. data/lib/rasn1/types/printable_string.rb +42 -0
  27. data/lib/rasn1/types/sequence.rb +105 -0
  28. data/lib/rasn1/types/sequence_of.rb +199 -0
  29. data/lib/rasn1/types/set.rb +28 -0
  30. data/lib/rasn1/types/set_of.rb +18 -0
  31. data/lib/rasn1/types/tag.rb +189 -0
  32. data/lib/rasn1/types/universal_string.rb +22 -0
  33. data/lib/rasn1/types/utc_time.rb +67 -0
  34. data/lib/rasn1/types/utf8_string.rb +21 -0
  35. data/lib/rasn1/types/visible_string.rb +30 -0
  36. data/lib/rasn1/types.rb +149 -0
  37. data/lib/rasn1/value_notation.rb +256 -0
  38. data/lib/rasn1/version.rb +6 -0
  39. data/lib/rasn1/wrapper.rb +279 -0
  40. data/lib/rasn1.rb +51 -0
  41. metadata +123 -0
@@ -0,0 +1,675 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # @abstract This is base class for all ASN.1 types.
6
+ #
7
+ # Subclasses SHOULD define:
8
+ # * an ID constant defining ASN.1 BER/DER identification number,
9
+ # * a method {#der_to_value} converting DER into {#value}.
10
+ # * a private method {#value_to_der} converting its {#value} to DER,
11
+ #
12
+ # ==Define an optional value
13
+ # An optional value may be defined using +:optional+ key from {#initialize}:
14
+ # Integer.new(:int, optional: true)
15
+ # An optional value implies:
16
+ # * while parsing, if decoded ID is not optional expected ID, no {ASN1Error}
17
+ # is raised, and parser tries next field,
18
+ # * while encoding, if {#value} is +nil+, this value is not encoded.
19
+ # ==Define a default value
20
+ # A default value may be defined using +:default+ key from {#initialize}:
21
+ # Integer.new(:int, default: 0)
22
+ # A default value implies:
23
+ # * while parsing, if decoded ID is not expected one, no {ASN1Error} is raised
24
+ # and parser sets default value to this ID. Then parser tries next field,
25
+ # * while encoding, if {#value} is equal to default value, this value is not
26
+ # encoded.
27
+ # ==Define a tagged value
28
+ # ASN.1 permits to define tagged values.
29
+ # By example:
30
+ # -- context specific tag
31
+ # CType ::= [0] EXPLICIT INTEGER
32
+ # -- application specific tag
33
+ # AType ::= [APPLICATION 1] EXPLICIT INTEGER
34
+ # -- private tag
35
+ # PType ::= [PRIVATE 2] EXPLICIT INTEGER
36
+ # These types may be defined as:
37
+ # ctype = RASN1::Types::Integer.new(explicit: 0) # with explicit, default #asn1_class is :context
38
+ # atype = RASN1::Types::Integer.new(explicit: 1, class: :application)
39
+ # ptype = RASN1::Types::Integer.new(explicit: 2, class: :private)
40
+ # Sometimes, an EXPLICIT type should be CONSTRUCTED. To do that, use +:constructed+
41
+ # option:
42
+ # ptype = RASN1::Types::Integer.new(explicit: 2, class: :private, constructed: true)
43
+ #
44
+ # Implicit tagged values may also be defined:
45
+ # ctype_implicit = RASN1::Types::Integer.new(implicit: 0)
46
+ # @author Sylvain Daubert
47
+ class Base # rubocop:disable Metrics/ClassLength
48
+ # Allowed ASN.1 classes
49
+ CLASSES = {
50
+ universal: 0x00,
51
+ application: 0x40,
52
+ context: 0x80,
53
+ private: 0xc0
54
+ }.freeze
55
+
56
+ def colorize(msg)
57
+ tracer ? tracer.colorize(msg) : Rainbow.new.wrap(msg)
58
+ end
59
+
60
+ include RASN1::Helpers::Colorize
61
+
62
+ # Binary mask to get class
63
+ # @private
64
+ CLASS_MASK = 0xc0
65
+
66
+ # @private first octet identifier for multi-octets identifier
67
+ MULTI_OCTETS_ID = 0x1f
68
+
69
+ # Length value for indefinite length
70
+ INDEFINITE_LENGTH = 0x80
71
+
72
+ # @return [String,nil]
73
+ attr_reader :name
74
+ # @return [Symbol]
75
+ attr_reader :asn1_class
76
+ # @return [Object,nil] default value, if defined
77
+ attr_reader :default
78
+ # @return [Hash[Symbol, Object]]
79
+ attr_reader :options
80
+ # @return [String] raw parsed data
81
+ attr_reader :raw_data
82
+ # @return [String] raw parsed length
83
+ attr_reader :raw_length
84
+
85
+ private :raw_data, :raw_length
86
+
87
+ def tracer
88
+ ::RASN1.tracer
89
+ end
90
+
91
+ # Get ASN.1 type
92
+ # @return [String]
93
+ def self.type
94
+ return @type if defined? @type
95
+
96
+ @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
97
+ end
98
+
99
+ # Get ASN.1 type used to encode this one
100
+ # @return [String]
101
+ def self.encoded_type
102
+ type
103
+ end
104
+
105
+ # Parse a DER or BER string
106
+ # @param [String] der_or_ber string to parse
107
+ # @param [Hash] options
108
+ # @return [Base]
109
+ # @option options [Boolean] :ber if +true+, parse a BER string, else a DER one
110
+ # @note More options are supported. See {Base#initialize}.
111
+ def self.parse(der_or_ber, options={})
112
+ obj = self.new(options)
113
+ obj.parse!(der_or_ber, ber: options[:ber])
114
+ obj
115
+ end
116
+
117
+ # Say if a type is constrained.
118
+ # Always return +false+ for predefined types
119
+ # @return [Booleran]
120
+ def self.constrained?
121
+ false
122
+ end
123
+
124
+ # @param [Hash] options
125
+ # @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
126
+ # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
127
+ # @option options [::Boolean] :optional define this tag as optional. Default
128
+ # is +false+
129
+ # @option options [Object] :default default value (ASN.1 DEFAULT)
130
+ # @option options [Object] :value value to set
131
+ # @option options [::Integer] :implicit define an IMPLICIT tagged type
132
+ # @option options [::Integer] :explicit define an EXPLICIT tagged type
133
+ # @option options [::Boolean] :constructed if +true+, set type as constructed.
134
+ # May only be used when +:explicit+ is defined, else it is discarded.
135
+ # @option options [::String] :name name for this node
136
+ def initialize(options={})
137
+ @constructed = nil
138
+ set_value(options.delete(:value))
139
+ self.options = options
140
+ specific_initializer
141
+ @raw_data = ''.b
142
+ @raw_length = ''.b
143
+ end
144
+
145
+ # @abstract To help subclass initialize itself. Default implementation do nothing.
146
+ def specific_initializer; end
147
+
148
+ # Deep copy @value and @default.
149
+ def initialize_copy(*)
150
+ super
151
+ @value = @value.dup
152
+ @no_value = @no_value.dup
153
+ @default = @default.dup
154
+ end
155
+
156
+ # Get value or default value
157
+ def value
158
+ if value?
159
+ @value
160
+ else
161
+ @default
162
+ end
163
+ end
164
+
165
+ # Set value. If +val+ is +nil+, unset value
166
+ # @param [Object,nil] val
167
+ def value=(val)
168
+ set_value(val)
169
+ end
170
+
171
+ # @abstract Define 'void' value (i.e. 'value' when no value was set)
172
+ def void_value
173
+ ''
174
+ end
175
+
176
+ # Say if this type is optional
177
+ # @return [::Boolean]
178
+ def optional?
179
+ @optional
180
+ end
181
+
182
+ # Say if this type is tagged or not
183
+ # @return [::Boolean]
184
+ def tagged?
185
+ !@tag.nil?
186
+ end
187
+
188
+ # Say if a tagged type is explicit
189
+ # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
190
+ # if explicit, else +false+
191
+ def explicit?
192
+ defined?(@tag) ? @tag == :explicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
193
+ end
194
+
195
+ # Say if a tagged type is implicit
196
+ # @return [::Boolean,nil] return +nil+ if not tagged, return +true+
197
+ # if implicit, else +false+
198
+ def implicit?
199
+ defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
200
+ end
201
+
202
+ # @abstract This method SHOULD be partly implemented by subclasses, which
203
+ # SHOULD respond to +#value_to_der+.
204
+ # @return [String] DER-formated string
205
+ def to_der
206
+ build
207
+ end
208
+
209
+ # @return [::Boolean] +true+ if this is a primitive type
210
+ def primitive?
211
+ (self.class < Primitive) && !@constructed
212
+ end
213
+
214
+ # @return [::Boolean] +true+ if this is a constructed type
215
+ def constructed?
216
+ (self.class < Constructed) || !!@constructed
217
+ end
218
+
219
+ # Get ASN.1 type
220
+ # @return [String]
221
+ def type
222
+ self.class.type
223
+ end
224
+
225
+ # Get identifier value
226
+ # @return [Integer]
227
+ def id
228
+ id_value
229
+ end
230
+
231
+ # @abstract This method SHOULD be partly implemented by subclasses to parse
232
+ # data. Subclasses SHOULD respond to +#der_to_value+.
233
+ # Parse a DER string. This method updates object.
234
+ # @param [String] der DER string
235
+ # @param [Boolean] ber if +true+, accept BER encoding
236
+ # @return [Integer] total number of parsed bytes
237
+ # @raise [ASN1Error] error on parsing
238
+ def parse!(der, ber: false)
239
+ total_length, data = do_parse(der, ber: ber)
240
+ return 0 if total_length.zero?
241
+
242
+ if explicit?
243
+ do_parse_explicit(data)
244
+ else
245
+ der_to_value(data, ber: ber)
246
+ end
247
+
248
+ total_length
249
+ end
250
+
251
+ # Give size in octets of encoded value
252
+ # @return [Integer]
253
+ def value_size
254
+ value_to_der.size
255
+ end
256
+
257
+ # @param [Integer] level
258
+ # @return [String]
259
+ def inspect(level=0, color: true)
260
+ str = common_inspect(level)
261
+ str << ' ' << inspect_value
262
+ str << ' OPTIONAL' if optional?
263
+ str << " DEFAULT #{@default}" unless @default.nil?
264
+ str
265
+ end
266
+
267
+ # Objects are equal if they have same class AND same DER
268
+ # @param [Base] other
269
+ # @return [Boolean]
270
+ def ==(other)
271
+ (other.class == self.class) && (other.to_der == self.to_der)
272
+ end
273
+
274
+ # Set options to this object
275
+ # @param [Hash] options
276
+ # @return [void]
277
+ # @since 0.12
278
+ def options=(options)
279
+ set_class options[:class]
280
+ set_optional options[:optional]
281
+ set_default options[:default]
282
+ set_tag options
283
+ @name = options[:name]
284
+ @options = options
285
+ end
286
+
287
+ # Say if a value is set
288
+ # @return [Boolean]
289
+ # @since 0.12.0
290
+ def value?
291
+ !@no_value
292
+ end
293
+
294
+ # Say if DER can be built (not default value, not optional without value, has a value)
295
+ # @return [Boolean]
296
+ # @since 0.12.0
297
+ def can_build?
298
+ value? && (@default.nil? || (@value != @default))
299
+ end
300
+
301
+ # @private Tracer private API
302
+ # @return [String]
303
+ def trace
304
+ return trace_real if value?
305
+
306
+ msg = msg_type
307
+ if default.nil? # rubocop:disable Style/ConditionalAssignment
308
+ msg << " #{colorize_nil}"
309
+ else
310
+ msg << " #{colorize_attribute('DEFAULT VALUE')} #{colorize_default(value)}"
311
+ end
312
+ end
313
+
314
+ # @private Parse tage and length from binary string. Return data length and binary data.
315
+ # @param [String] der
316
+ # @param [Boolean] ber
317
+ # @return [Array(::Integer, String)]
318
+ # @since 0.15.0 was private before
319
+ def do_parse(der, ber: false)
320
+ return [0, ''] unless check_id(der)
321
+
322
+ id_size = Types.decode_identifier_octets(der).last
323
+ total_length, data = get_data(der[id_size..], ber)
324
+ total_length += id_size
325
+ @no_value = false
326
+
327
+ [total_length, data]
328
+ end
329
+
330
+ # @private Delegate to #explicit type to generate sub-value
331
+ # @param [String] data
332
+ # @return [void]
333
+ # @since 0.15.0 was private before
334
+ def do_parse_explicit(data)
335
+ type = explicit_type
336
+ type.parse!(data)
337
+ @value = type.value
338
+ end
339
+
340
+ # Make value from DER/BER string
341
+ # @param [String] der
342
+ # @param [::Boolean] ber
343
+ # @return [void]
344
+ # @since 0.15.0 was private before
345
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
346
+ @value = der
347
+ end
348
+
349
+ private
350
+
351
+ def trace_real
352
+ encoded_id = unpack(encode_identifier_octets)
353
+ data_length = raw_data.length
354
+ encoded_length = unpack(raw_length)
355
+ msg = "#{msg_type} #{parens_hex(encoded_id)}, #{length_specifier(data_length, encoded_length)}"
356
+ msg << trace_data
357
+ end
358
+
359
+ def trace_data(data=nil)
360
+ byte_count = 0
361
+ data ||= raw_data
362
+
363
+ lines = []
364
+ str = ''
365
+ data.each_byte do |byte|
366
+ if (byte_count % 16).zero?
367
+ str = trace_format_new_data_line(byte_count)
368
+ lines << str
369
+ end
370
+ str[compute_trace_index(byte_count, 3), 2] = '%02x' % byte
371
+ str[compute_trace_index(byte_count, 1, 49)] = byte.between?(32, 126) ? byte.chr : '.'
372
+ byte_count += 1
373
+ end
374
+ lines.map(&:rstrip).join << "\n"
375
+ end
376
+
377
+ def trace_format_new_data_line(count)
378
+ head_line = RASN1.tracer.indent(RASN1.tracer.tracing_level + 1)
379
+ ("\n#{head_line}%04x " % count) << ' ' * 68
380
+ end
381
+
382
+ def compute_trace_index(byte_count, byte_count_mul=1, offset=0)
383
+ base_idx = 7 + RASN1.tracer.indent(RASN1.tracer.tracing_level + 1).length
384
+ base_idx + offset + (byte_count % 16) * byte_count_mul
385
+ end
386
+
387
+ def unpack(binstr)
388
+ binstr.unpack1('H*')
389
+ end
390
+
391
+ def asn1_class_to_s
392
+ asn1_class == :universal ? '' : asn1_class.to_s.upcase << ' '
393
+ end
394
+
395
+ def msg_type(no_id: false)
396
+ msg = name.nil? ? +'' : "#{colorize_name(name)} "
397
+ msg << "#{colorize_id(id, asn1_class_to_s)} " unless no_id
398
+ msg << if explicit?
399
+ + "#{colorize_attribute('EXPLICIT')} "
400
+ elsif implicit?
401
+ + "#{colorize_attribute('IMPLICIT')} "
402
+ else
403
+ +''
404
+ end
405
+ msg << colorize_class(type)
406
+ msg << " #{colorize_attribute('OPTIONAL')}" if optional?
407
+ msg
408
+ end
409
+
410
+ def pc_bit
411
+ if @constructed.nil?
412
+ self.class.const_get(:ASN1_PC)
413
+ elsif @constructed # true
414
+ Constructed::ASN1_PC
415
+ else # false
416
+ Primitive::ASN1_PC
417
+ end
418
+ end
419
+
420
+ def common_inspect(level)
421
+ lvl = [level, 0].max
422
+ str = ' ' * lvl
423
+ str << "#{@name} " unless @name.nil?
424
+ str << asn1_class_to_s
425
+ str << "[#{id}] EXPLICIT " if explicit?
426
+ str << "[#{id}] IMPLICIT " if implicit?
427
+ str << "#{type}:"
428
+ end
429
+
430
+ def inspect_value
431
+ if value?
432
+ value.inspect
433
+ else
434
+ '(NO VALUE)'
435
+ end
436
+ end
437
+
438
+ def value_to_der
439
+ case @value
440
+ when Base
441
+ @value.to_der
442
+ else
443
+ @value.to_s
444
+ end
445
+ end
446
+
447
+ def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
448
+ case asn1_class
449
+ when nil
450
+ @asn1_class = :universal
451
+ when Symbol
452
+ raise ClassError unless CLASSES.key?(asn1_class)
453
+
454
+ @asn1_class = asn1_class
455
+ else
456
+ raise ClassError
457
+ end
458
+ end
459
+
460
+ def set_optional(optional) # rubocop:disable Naming/AccessorMethodName
461
+ @optional = !!optional
462
+ end
463
+
464
+ def set_default(default) # rubocop:disable Naming/AccessorMethodName
465
+ @default = default
466
+ end
467
+
468
+ # handle undocumented option +:tag_value+, used internally by
469
+ # {RASN1.parse} to parse non-universal class tags.
470
+ def set_tag(options) # rubocop:disable Naming/AccessorMethodName
471
+ @constructed = options[:constructed]
472
+ if options[:explicit]
473
+ @tag = :explicit
474
+ @id_value = tag_id_to_integer(options[:explicit])
475
+ elsif options[:implicit]
476
+ @tag = :implicit
477
+ @id_value = tag_id_to_integer(options[:implicit])
478
+ elsif options[:tag_value]
479
+ @id_value = tag_id_to_integer(options[:tag_value])
480
+ end
481
+
482
+ @asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
483
+ end
484
+
485
+ # Convert a tag id to an integer.
486
+ # If given a String or Symbol, interprets the characters as big-endian octets
487
+ # (like Apple 4-character codes / 4CCs). Integers are returned as-is.
488
+ # @param [::Integer, String, Symbol] value tag value
489
+ # @return [::Integer]
490
+ def tag_id_to_integer(value)
491
+ case value
492
+ when ::Integer
493
+ value
494
+ when ::String, ::Symbol
495
+ value.to_s.bytes.reduce(0) { |acc, b| (acc << 8) | b }
496
+ else
497
+ value
498
+ end
499
+ end
500
+
501
+ def set_value(value) # rubocop:disable Naming/AccessorMethodName
502
+ if value.nil?
503
+ @no_value = true
504
+ @value = void_value
505
+ else
506
+ @no_value = false
507
+ @value = value
508
+ end
509
+ value
510
+ end
511
+
512
+ def build
513
+ if can_build?
514
+ if explicit?
515
+ v = explicit_type
516
+ v.value = @value
517
+ encoded_value = v.to_der
518
+ else
519
+ encoded_value = value_to_der
520
+ end
521
+ encode_identifier_octets << encode_size(encoded_value.size) << encoded_value
522
+ else
523
+ ''
524
+ end
525
+ end
526
+
527
+ def id_value
528
+ return @id_value if defined?(@id_value) && !@id_value.nil?
529
+
530
+ self.class.const_get(:ID)
531
+ end
532
+
533
+ def encode_identifier_octets
534
+ id2octets.pack('C*')
535
+ end
536
+
537
+ def id2octets
538
+ first_octet = CLASSES[asn1_class] | pc_bit
539
+ if id < MULTI_OCTETS_ID
540
+ [first_octet | id]
541
+ else
542
+ [first_octet | MULTI_OCTETS_ID] + unsigned_to_chained_octets(id)
543
+ end
544
+ end
545
+
546
+ # Encode an unsigned integer on multiple octets.
547
+ # Value is encoded on bit 6-0 of each octet, bit 7(MSB) indicates wether
548
+ # further octets follow.
549
+ def unsigned_to_chained_octets(value)
550
+ ary = []
551
+ while value.positive?
552
+ ary.unshift(value & 0x7f | 0x80)
553
+ value >>= 7
554
+ end
555
+ ary[-1] &= 0x7f
556
+ ary
557
+ end
558
+
559
+ def encode_size(size)
560
+ if size >= INDEFINITE_LENGTH
561
+ bytes = []
562
+ while size > 255
563
+ bytes.unshift(size & 0xff)
564
+ size >>= 8
565
+ end
566
+ bytes.unshift(size)
567
+ bytes.unshift(INDEFINITE_LENGTH | bytes.size)
568
+ bytes.pack('C*')
569
+ else
570
+ [size].pack('C')
571
+ end
572
+ end
573
+
574
+ # Check ID from +der+ is the one expected
575
+ # @return [Boolean] +true+ is ID is expected, +false+ if it is not but +self+ is {#optional?}
576
+ # or has a {#default} value.
577
+ # @raise [ASN1Error] ID was not expected, is not optional and has no default value
578
+ def check_id(der) # rubocop:disable Naming/PredicateMethod
579
+ expected_id = encode_identifier_octets
580
+ real_id = der[0, expected_id.size]
581
+ return true if real_id == expected_id
582
+
583
+ if optional?
584
+ @no_value = true
585
+ @value = void_value
586
+ elsif !@default.nil?
587
+ @value = @default
588
+ else
589
+ raise_id_error(der)
590
+ end
591
+ false
592
+ end
593
+
594
+ def get_data(der, ber)
595
+ return [0, ''] if der.nil? || der.empty?
596
+
597
+ length, length_length = get_length(der, ber)
598
+
599
+ data = der[1 + length_length, length]
600
+ @raw_length = der[0, length_length + 1]
601
+ @raw_data = data
602
+
603
+ total_length = 1 + length
604
+ total_length += length_length if length_length.positive?
605
+
606
+ [total_length, data]
607
+ end
608
+
609
+ def get_length(der, ber)
610
+ length = der.unpack1('C').to_i
611
+ length_length = 0
612
+
613
+ if length == INDEFINITE_LENGTH
614
+ raise_on_indefinite_length(ber)
615
+ elsif length > INDEFINITE_LENGTH
616
+ length_length = length & 0x7f
617
+ length = der[1, length_length].unpack('C*')
618
+ .reduce(0) { |len, b| (len << 8) | b }
619
+ end
620
+
621
+ [length, length_length]
622
+ end
623
+
624
+ def raise_on_indefinite_length(ber)
625
+ if primitive?
626
+ raise ASN1Error, "malformed #{type}: indefinite length " \
627
+ 'forbidden for primitive types'
628
+ elsif ber
629
+ raise NotImplementedError, 'indefinite length not supported'
630
+ else
631
+ raise ASN1Error, 'indefinite length forbidden in DER encoding'
632
+ end
633
+ end
634
+
635
+ def explicit_type
636
+ self.class.new(name: name)
637
+ end
638
+
639
+ def raise_id_error(der)
640
+ msg = name.nil? ? +'' : "#{name}: "
641
+ msg << "Expected #{self2name} but get #{der2name(der)}"
642
+
643
+ raise ASN1Error, msg
644
+ end
645
+
646
+ def self2name
647
+ name = "#{colorize_class(asn1_class.to_s.upcase)} #{colorize_attribute(constructed? ? 'CONSTRUCTED' : 'PRIMITIVE')}"
648
+ if implicit? || explicit?
649
+ name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
650
+ else
651
+ name << ' ' << colorize_class(self.class.type)
652
+ end
653
+ end
654
+
655
+ def der2name(der)
656
+ return 'no ID' if der.nil? || der.empty?
657
+
658
+ asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
659
+ name = "#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
660
+ type = find_type(id)
661
+ name << " #{type.nil? ? "#{colorize(bin2hex(der[0...id_size])).magenta}" : colorize(type.encoded_type).green}"
662
+ end
663
+
664
+ def find_type(id)
665
+ Types.constants.map { |c| Types.const_get(c) }
666
+ .select { |klass| klass < Primitive || klass < Constructed }
667
+ .find { |klass| id == klass::ID }
668
+ end
669
+
670
+ def bin2hex(str)
671
+ str.unpack1('H*')
672
+ end
673
+ end
674
+ end
675
+ end