rasn1 0.7.1 → 0.8.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f51c0d098d5fde894990bb1637582548b5a35ae47c39e9d006971321501bbc49
4
- data.tar.gz: f14a8c3fabfedd6eeeb02b80c6d4dd4a69f67b34efb283d592d92492143560f5
3
+ metadata.gz: f4eb9957878c25e54a394fee02aab5a42aa1201ad6250f7f6a2a3992128c87d6
4
+ data.tar.gz: 3b3d3c85d619ee7f78923d7f7c839874f329087b0b1a4094839b059daf94c2ec
5
5
  SHA512:
6
- metadata.gz: 99e94933fdf79ac6cc413e2f2ca85d9f73ecb493444a20b61394bde44418ebe6dfd691eb59e65166c00247c8fb870686ded69ff77cd00662549835fa1aab4578
7
- data.tar.gz: 2ac5e04d46e2fe7056a94489f99f1903ae1a1c645344306d548c1cc38dae0951f73f2936357ea27695f640c6f39593086c8927d38edcffa9d9ec6cb31ed30b3f
6
+ metadata.gz: 9eb501aacf629355e56a4103dfc3e354bfa035c031a23f2dcc61f8add191a13960cd64ea68db36340694f3b20aba316c0088ebe91943d795405b11489be9342e
7
+ data.tar.gz: 115cef9248e5251a24ec0bb6d6e977084cfb4ff78118a7f5be31ee8fea05a0c831b935fa72c18bdc08e71bb729a85cff2b394f4a84aafd56245f70908f0a4dc6
@@ -44,7 +44,7 @@ module RASN1
44
44
  def self.parse(der, ber: false)
45
45
  root = nil
46
46
  until der.empty?
47
- type = Types.tag2type(der[0].ord)
47
+ type = Types.id2type(der)
48
48
  type.parse!(der, ber: ber)
49
49
  root = type if root.nil?
50
50
 
@@ -204,7 +204,7 @@ module RASN1
204
204
  # +Object#object_id+.
205
205
  # @see Types::ObjectId#initialize
206
206
  def objectid(name, options={})
207
- options.merge!(name: name)
207
+ options[:name] = name
208
208
  proc = proc { |opts| Types::ObjectId.new(options.merge(opts)) }
209
209
  @root = [name, proc]
210
210
  end
@@ -213,7 +213,7 @@ module RASN1
213
213
  # @param [Hash] options
214
214
  # @see Types::Any#initialize
215
215
  def any(name, options={})
216
- options.merge!(name: name)
216
+ options[:name] = name
217
217
  proc = proc { |opts| Types::Any.new(options.merge(opts)) }
218
218
  @root = [name, proc]
219
219
  end
@@ -369,7 +369,7 @@ module RASN1
369
369
  elt = self[name]
370
370
  return elt unless elt.nil?
371
371
 
372
- keys.each do |subelt_name|
372
+ @elements.each_key do |subelt_name|
373
373
  if self[subelt_name].is_a?(Model)
374
374
  elt = self[subelt_name][name]
375
375
  return elt unless elt.nil?
@@ -408,40 +408,37 @@ module RASN1
408
408
  @elements[name].value = content.map do |name2, proc_or_class, content2|
409
409
  subel = get_type(proc_or_class)
410
410
  @elements[name2] = subel
411
- if composed?(subel) && content2.is_a?(Array)
412
- set_elements(name2, proc_or_class, content2)
413
- end
411
+ set_elements(name2, proc_or_class, content2) if composed?(subel) && content2.is_a?(Array)
414
412
  subel
415
413
  end
416
414
  end
417
415
 
418
416
  def initialize_elements(obj, args)
419
417
  args.each do |name, value|
420
- if obj[name]
421
- if value.is_a? Hash
422
- if obj[name].is_a? Model
423
- initialize_elements obj[name], value
424
- else
425
- raise ArgumentError, "element #{name}: may only pass a Hash for Model elements"
426
- end
427
- elsif value.is_a? Array
428
- composed = if obj[name].is_a? Model
429
- obj[name].root
430
- else
431
- obj[name]
432
- end
433
- if composed.of_type.is_a? Model
434
- value.each do |el|
435
- composed << initialize_elements(composed.of_type.class.new, el)
436
- end
437
- else
438
- value.each do |el|
439
- obj[name] << el
440
- end
418
+ next unless obj[name]
419
+
420
+ case value
421
+ when Hash
422
+ raise ArgumentError, "element #{name}: may only pass a Hash for Model elements" unless obj[name].is_a? Model
423
+
424
+ initialize_elements obj[name], value
425
+ when Array
426
+ composed = if obj[name].is_a? Model
427
+ obj[name].root
428
+ else
429
+ obj[name]
430
+ end
431
+ if composed.of_type.is_a? Model
432
+ value.each do |el|
433
+ composed << initialize_elements(composed.of_type.class.new, el)
441
434
  end
442
435
  else
443
- obj[name].value = value
436
+ value.each do |el|
437
+ obj[name] << el
438
+ end
444
439
  end
440
+ else
441
+ obj[name].value = value
445
442
  end
446
443
  end
447
444
  end
@@ -18,32 +18,58 @@ module RASN1
18
18
  .select { |klass| klass < Constructed }
19
19
  end
20
20
 
21
- # Give ASN.1 type from an integer. If +tag+ is unknown, return a {Types::Base}
21
+ # @private
22
+ # Decode a DER string to extract identifier octets.
23
+ # @param [String] der
24
+ # @return [Array] Return ASN.1 class as Symbol, contructed/primitive as Symbol,
25
+ # ID and size of identifier octets
26
+ def self.decode_identifier_octets(der)
27
+ first_octet = der[0].unpack1('C')
28
+ asn1_class = Types::Base::CLASSES.key(first_octet & Types::Base::CLASS_MASK)
29
+ pc = (first_octet & Types::Constructed::ASN1_PC).positive? ? :constructed : :primitive
30
+ id = first_octet & Types::Base::MULTI_OCTETS_ID
31
+
32
+ size = if id == Types::Base::MULTI_OCTETS_ID
33
+ id = 0
34
+ 1.upto(der.size - 1) do |i|
35
+ octet = der[i].unpack1('C')
36
+ id = (id << 7) | (octet & 0x7f)
37
+ break i + 1 if (octet & 0x80).zero?
38
+ end
39
+ else
40
+ 1
41
+ end
42
+
43
+ [asn1_class, pc, id, size]
44
+ end
45
+
46
+ # Give ASN.1 type from a DER string. If ID is unknown, return a {Types::Base}
22
47
  # object.
23
- # @param [Integer] tag
48
+ # @param [String] der
24
49
  # @return [Types::Base]
25
50
  # @raise [ASN1Error] +tag+ is out of range
26
- def self.tag2type(tag)
27
- raise ASN1Error, 'tag is out of range' if tag > 0xff
28
-
29
- unless defined? @tag2types
51
+ def self.id2type(der)
52
+ # Define a cache for well-known ASN.1 types
53
+ unless defined? @id2types
30
54
  constructed = self.constructed - [Types::SequenceOf, Types::SetOf]
31
55
  primitives = self.primitives - [Types::Enumerated]
32
56
  ary = (primitives + constructed).map do |type|
33
- next unless type.const_defined? :TAG
57
+ next unless type.const_defined? :ID
34
58
 
35
- [type::TAG, type]
59
+ [type::ID, type]
36
60
  end
37
- @tag2types = Hash[ary]
38
- @tag2types.default = Types::Base
39
- @tag2types.freeze
61
+ @id2types = Hash[ary]
62
+ @id2types.default = Types::Base
63
+ @id2types.freeze
40
64
  end
41
65
 
42
- klass = @tag2types[tag & 0xdf] # Remove CONSTRUCTED bit
43
- is_constructed = (tag & 0x20) == 0x20
44
- asn1class = Types::Base::CLASSES.key(tag & 0xc0)
66
+ asn1class, pc, id, = self.decode_identifier_octets(der)
67
+ # cache_id: check versus class and 5 LSB bits
68
+ cache_id = der.unpack1('C') & 0xdf
69
+ klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::Base
70
+ is_constructed = (pc == :constructed)
45
71
  options = { class: asn1class, constructed: is_constructed }
46
- options[:tag_value] = (tag & 0x1f) if klass == Types::Base
72
+ options[:tag_value] = id if klass == Types::Base
47
73
  klass.new(options)
48
74
  end
49
75
  end
@@ -31,8 +31,12 @@ module RASN1
31
31
  raise ASN1Error, 'Expected ANY but get nothing'
32
32
  end
33
33
 
34
- total_length, = get_data(der, ber)
34
+ id_size = Types.decode_identifier_octets(der).last
35
+ total_length, = get_data(der[id_size..-1], ber)
36
+ total_length += id_size
37
+
35
38
  @value = der[0, total_length]
39
+
36
40
  total_length
37
41
  end
38
42
 
@@ -5,7 +5,7 @@ module RASN1
5
5
  # @abstract This is base class for all ASN.1 types.
6
6
  #
7
7
  # Subclasses SHOULD define:
8
- # * a TAG constant defining ASN.1 tag number,
8
+ # * an ID constant defining ASN.1 BER/DER identification number,
9
9
  # * a private method {#value_to_der} converting its {#value} to DER,
10
10
  # * a private method {#der_to_value} converting DER into {#value}.
11
11
  #
@@ -13,15 +13,15 @@ module RASN1
13
13
  # An optional value may be defined using +:optional+ key from {#initialize}:
14
14
  # Integer.new(:int, optional: true)
15
15
  # An optional value implies:
16
- # * while parsing, if decoded tag is not optional expected tag, no {ASN1Error}
17
- # is raised, and parser tries net tag,
16
+ # * while parsing, if decoded ID is not optional expected ID, no {ASN1Error}
17
+ # is raised, and parser tries next field,
18
18
  # * while encoding, if {#value} is +nil+, this value is not encoded.
19
19
  # ==Define a default value
20
20
  # A default value may be defined using +:default+ key from {#initialize}:
21
21
  # Integer.new(:int, default: 0)
22
22
  # A default value implies:
23
- # * while parsing, if decoded tag is not expected tag, no {ASN1Error} is raised
24
- # and parser sets default value to this tag. Then parser tries nex tag,
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
25
  # * while encoding, if {#value} is equal to default value, this value is not
26
26
  # encoded.
27
27
  # ==Define a tagged value
@@ -45,30 +45,20 @@ module RASN1
45
45
  # ctype_implicit = RASN1::Types::Integer.new(implicit: 0)
46
46
  # @author Sylvain Daubert
47
47
  class Base
48
- # Allowed ASN.1 tag classes
48
+ # Allowed ASN.1 classes
49
49
  CLASSES = {
50
- universal: 0x00,
50
+ universal: 0x00,
51
51
  application: 0x40,
52
- context: 0x80,
53
- private: 0xc0
52
+ context: 0x80,
53
+ private: 0xc0
54
54
  }.freeze
55
55
 
56
- # @private Types that cannot be dupped (Ruby <= 2.3)
57
- UNDUPPABLE_TYPES = [[NilClass, nil], [TrueClass, true], [FalseClass, false], [Integer, 0]].map do |klass, obj|
58
- begin
59
- obj.dup
60
- nil
61
- rescue => TypeError
62
- klass
63
- end
64
- end.compact.freeze
65
-
66
56
  # Binary mask to get class
67
57
  # @private
68
58
  CLASS_MASK = 0xc0
69
59
 
70
- # Maximum ASN.1 tag number
71
- MAX_TAG = 0x1e
60
+ # @private first octet identifier for multi-octets identifier
61
+ MULTI_OCTETS_ID = 0x1f
72
62
 
73
63
  # Length value for indefinite length
74
64
  INDEFINITE_LENGTH = 0x80
@@ -92,7 +82,7 @@ module RASN1
92
82
 
93
83
  # Get ASN.1 type used to encode this one
94
84
  # @return [String]
95
- def self.encode_type
85
+ def self.encoded_type
96
86
  type
97
87
  end
98
88
 
@@ -109,11 +99,11 @@ module RASN1
109
99
 
110
100
  # @overload initialize(options={})
111
101
  # @param [Hash] options
112
- # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
102
+ # @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
113
103
  # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
114
104
  # @option options [::Boolean] :optional define this tag as optional. Default
115
105
  # is +false+
116
- # @option options [Object] :default default value for DEFAULT tag
106
+ # @option options [Object] :default default value (ASN.1 DEFAULT)
117
107
  # @option options [Object] :value value to set
118
108
  # @option options [::Integer] :implicit define an IMPLICIT tagged type
119
109
  # @option options [::Integer] :explicit define an EXPLICIT tagged type
@@ -123,11 +113,11 @@ module RASN1
123
113
  # @overload initialize(value, options={})
124
114
  # @param [Object] value value to set for this ASN.1 object
125
115
  # @param [Hash] options
126
- # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
116
+ # @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
127
117
  # If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
128
- # @option options [::Boolean] :optional define this tag as optional. Default
118
+ # @option options [::Boolean] :optional define this value as optional. Default
129
119
  # is +false+
130
- # @option options [Object] :default default value for DEFAULT tag
120
+ # @option options [Object] :default default value (ASN.1 DEFAULT)
131
121
  # @option options [::Integer] :implicit define an IMPLICIT tagged type
132
122
  # @option options [::Integer] :explicit define an EXPLICIT tagged type
133
123
  # @option options [::Boolean] :constructed if +true+, set type as constructed.
@@ -145,8 +135,8 @@ module RASN1
145
135
 
146
136
  # Used by +#dup+ and +#clone+. Deep copy @value and @default.
147
137
  def initialize_copy(_other)
148
- @value = @value.dup unless UNDUPPABLE_TYPES.include?(@value.class)
149
- @default = @default.dup unless UNDUPPABLE_TYPES.include?(@default.class)
138
+ @value = @value.dup
139
+ @default = @default.dup
150
140
  end
151
141
 
152
142
  # Get value or default value
@@ -187,7 +177,7 @@ module RASN1
187
177
  # SHOULD respond to +#value_to_der+.
188
178
  # @return [String] DER-formated string
189
179
  def to_der
190
- build_tag
180
+ build
191
181
  end
192
182
 
193
183
  # @return [::Boolean] +true+ if this is a primitive type
@@ -206,17 +196,10 @@ module RASN1
206
196
  self.class.type
207
197
  end
208
198
 
209
- # Get tag value
199
+ # Get identifier value
210
200
  # @return [Integer]
211
- def tag
212
- pc = if @constructed.nil?
213
- self.class::ASN1_PC
214
- elsif @constructed # true
215
- Constructed::ASN1_PC
216
- else # false
217
- 0
218
- end
219
- tag_value | CLASSES[@asn1_class] | pc
201
+ def id
202
+ id_value
220
203
  end
221
204
 
222
205
  # @abstract This method SHOULD be partly implemented by subclasses to parse
@@ -227,11 +210,13 @@ module RASN1
227
210
  # @return [Integer] total number of parsed bytes
228
211
  # @raise [ASN1Error] error on parsing
229
212
  def parse!(der, ber: false)
230
- return 0 unless check_tag(der)
213
+ return 0 unless check_id(der)
231
214
 
232
- total_length, data = get_data(der, ber)
215
+ id_size = Types.decode_identifier_octets(der).last
216
+ total_length, data = get_data(der[id_size..-1], ber)
217
+ total_length += id_size
233
218
  if explicit?
234
- # Delegate to #explicit type to generate sub-tag
219
+ # Delegate to #explicit type to generate sub-value
235
220
  type = explicit_type
236
221
  type.value = @value
237
222
  type.parse!(data)
@@ -268,6 +253,16 @@ module RASN1
268
253
 
269
254
  private
270
255
 
256
+ def pc_bit
257
+ if @constructed.nil?
258
+ self.class::ASN1_PC
259
+ elsif @constructed # true
260
+ Constructed::ASN1_PC
261
+ else # false
262
+ Primitive::ASN1_PC
263
+ end
264
+ end
265
+
271
266
  def common_inspect(level)
272
267
  lvl = level >= 0 ? level : 0
273
268
  str = ' ' * lvl
@@ -295,6 +290,7 @@ module RASN1
295
290
  set_tag options
296
291
  @value = options[:value]
297
292
  @name = options[:name]
293
+ @options = options
298
294
  end
299
295
 
300
296
  def set_class(asn1_class)
@@ -302,7 +298,7 @@ module RASN1
302
298
  when nil
303
299
  @asn1_class = :universal
304
300
  when Symbol
305
- raise ClassError unless CLASSES.keys.include? asn1_class
301
+ raise ClassError unless CLASSES.key? asn1_class
306
302
 
307
303
  @asn1_class = asn1_class
308
304
  else
@@ -323,27 +319,27 @@ module RASN1
323
319
  def set_tag(options)
324
320
  if options[:explicit]
325
321
  @tag = :explicit
326
- @tag_value = options[:explicit]
322
+ @id_value = options[:explicit]
327
323
  @constructed = options[:constructed]
328
324
  elsif options[:implicit]
329
325
  @tag = :implicit
330
- @tag_value = options[:implicit]
326
+ @id_value = options[:implicit]
331
327
  @constructed = options[:constructed]
332
328
  elsif options[:tag_value]
333
- @tag_value = options[:tag_value]
329
+ @id_value = options[:tag_value]
334
330
  @constructed = options[:constructed]
335
331
  end
336
332
 
337
333
  @asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
338
334
  end
339
335
 
340
- def build_tag?
336
+ def can_build?
341
337
  !(!@default.nil? && (@value.nil? || (@value == @default))) &&
342
338
  !(optional? && @value.nil?)
343
339
  end
344
340
 
345
- def build_tag
346
- if build_tag?
341
+ def build
342
+ if can_build?
347
343
  if explicit?
348
344
  v = explicit_type
349
345
  v.value = @value
@@ -351,22 +347,42 @@ module RASN1
351
347
  else
352
348
  encoded_value = value_to_der
353
349
  end
354
- encode_tag << encode_size(encoded_value.size) << encoded_value
350
+ encode_identifier_octets << encode_size(encoded_value.size) << encoded_value
355
351
  else
356
352
  ''
357
353
  end
358
354
  end
359
355
 
360
- def tag_value
361
- return @tag_value if defined? @tag_value
356
+ def id_value
357
+ return @id_value if defined? @id_value
358
+
359
+ self.class::ID
360
+ end
362
361
 
363
- self.class::TAG
362
+ def encode_identifier_octets
363
+ id2octets.pack('C*')
364
364
  end
365
365
 
366
- def encode_tag
367
- raise ASN1Error, 'multi-byte tag value are not supported' unless tag_value <= MAX_TAG
366
+ def id2octets
367
+ first_octet = CLASSES[asn1_class] | pc_bit
368
+ if id < MULTI_OCTETS_ID
369
+ [first_octet | id]
370
+ else
371
+ [first_octet | MULTI_OCTETS_ID] + unsigned_to_chained_octets(id)
372
+ end
373
+ end
368
374
 
369
- [tag].pack('C')
375
+ # Encode an unsigned integer on multiple octets.
376
+ # Value is encoded on bit 6-0 of each octet, bit 7(MSB) indicates wether
377
+ # further octets follow.
378
+ def unsigned_to_chained_octets(value)
379
+ ary = []
380
+ while value.positive?
381
+ ary.unshift(value & 0x7f | 0x80)
382
+ value >>= 7
383
+ end
384
+ ary[-1] &= 0x7f
385
+ ary
370
386
  end
371
387
 
372
388
  def encode_size(size)
@@ -384,15 +400,16 @@ module RASN1
384
400
  end
385
401
  end
386
402
 
387
- def check_tag(der)
388
- tag = der[0, 1]
389
- if tag != encode_tag
403
+ def check_id(der)
404
+ expected_id = encode_identifier_octets
405
+ real_id = der[0, expected_id.size]
406
+ if real_id != expected_id
390
407
  if optional?
391
408
  @value = nil
392
409
  elsif !@default.nil?
393
410
  @value = @default
394
411
  else
395
- raise_tag_error(tag)
412
+ raise_id_error(der)
396
413
  end
397
414
  false
398
415
  else
@@ -401,30 +418,28 @@ module RASN1
401
418
  end
402
419
 
403
420
  def get_data(der, ber)
404
- length = der[1, 1].unpack('C').first
421
+ length = der[0, 1].unpack1('C')
405
422
  length_length = 0
406
423
 
407
424
  if length == INDEFINITE_LENGTH
408
425
  if primitive?
409
- raise ASN1Error, "malformed #{type} TAG: indefinite length " \
426
+ raise ASN1Error, "malformed #{type}: indefinite length " \
410
427
  'forbidden for primitive types'
411
428
  elsif ber
412
- raise NotImplementedError, 'TAG: indefinite length not ' \
413
- 'supported yet'
429
+ raise NotImplementedError, 'indefinite length not supported'
414
430
  else
415
- raise ASN1Error, 'TAG: indefinite length forbidden in DER ' \
416
- 'encoding'
431
+ raise ASN1Error, 'indefinite length forbidden in DER encoding'
417
432
  end
418
433
  elsif length < INDEFINITE_LENGTH
419
- data = der[2, length]
434
+ data = der[1, length]
420
435
  else
421
436
  length_length = length & 0x7f
422
- length = der[2, length_length].unpack('C*')
437
+ length = der[1, length_length].unpack('C*')
423
438
  .reduce(0) { |len, b| (len << 8) | b }
424
- data = der[2 + length_length, length]
439
+ data = der[1 + length_length, length]
425
440
  end
426
441
 
427
- total_length = 2 + length
442
+ total_length = 1 + length
428
443
  total_length += length_length if length_length.positive?
429
444
 
430
445
  [total_length, data]
@@ -434,36 +449,38 @@ module RASN1
434
449
  self.class.new
435
450
  end
436
451
 
437
- def raise_tag_error(tag)
452
+ def raise_id_error(der)
438
453
  msg = name.nil? ? +'' : +"#{name}: "
439
- msg << "Expected #{self2name} but get #{tag2name(tag)}"
454
+ msg << "Expected #{self2name} but get #{der2name(der)}"
440
455
  raise ASN1Error, msg
441
456
  end
442
457
 
443
- def class_from_numeric_tag(tag)
444
- CLASSES.key(tag & CLASS_MASK)
458
+ def class_from_numeric_id(id)
459
+ CLASSES.key(id & CLASS_MASK)
445
460
  end
446
461
 
447
462
  def self2name
448
- name = class_from_numeric_tag(tag).to_s.upcase
449
- name << " #{(tag & Constructed::ASN1_PC).positive? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
463
+ name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
450
464
  if implicit? || explicit?
451
- name << ' 0x%02X (0x%02X)' % [tag & 0x1f, tag]
465
+ name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
452
466
  else
453
467
  name << ' ' << self.class.type
454
468
  end
455
469
  end
456
470
 
457
- def tag2name(tag)
458
- return 'no tag' if tag.nil? || tag.empty?
471
+ def der2name(der)
472
+ return 'no ID' if der.nil? || der.empty?
459
473
 
460
- itag = tag.unpack('C').first
461
- name = class_from_numeric_tag(itag).to_s.upcase
462
- name << " #{(itag & Constructed::ASN1_PC).positive? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
474
+ asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
475
+ name = +"#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
463
476
  type = Types.constants.map { |c| Types.const_get(c) }
464
477
  .select { |klass| klass < Primitive || klass < Constructed }
465
- .find { |klass| klass::TAG == itag & 0x1f }
466
- name << " #{type.nil? ? '0x%02X (0x%02X)' % [itag & 0x1f, itag] : type.encode_type}"
478
+ .find { |klass| klass::ID == id }
479
+ name << " #{type.nil? ? '0x%X (0x%s)' % [id, bin2hex(der[0...id_size])] : type.encoded_type}"
480
+ end
481
+
482
+ def bin2hex(str)
483
+ str.unpack1('H*')
467
484
  end
468
485
  end
469
486
  end