rasn1 0.7.1 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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