rasn1 0.6.8 → 0.7.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.
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # @abstract This is base class for all ASN.1 types.
5
6
  #
6
7
  # Subclasses SHOULD define:
@@ -46,11 +47,25 @@ module RASN1
46
47
  class Base
47
48
  # Allowed ASN.1 tag classes
48
49
  CLASSES = {
49
- universal: 0x00,
50
- application: 0x40,
51
- context: 0x80,
52
- private: 0xc0
53
- }
50
+ universal: 0x00,
51
+ application: 0x40,
52
+ context: 0x80,
53
+ private: 0xc0
54
+ }.freeze
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
+ # Binary mask to get class
67
+ # @private
68
+ CLASS_MASK = 0xc0
54
69
 
55
70
  # Maximum ASN.1 tag number
56
71
  MAX_TAG = 0x1e
@@ -71,6 +86,7 @@ module RASN1
71
86
  # @return [String]
72
87
  def self.type
73
88
  return @type if defined? @type
89
+
74
90
  @type = self.to_s.gsub(/.*::/, '').gsub(/([a-z0-9])([A-Z])/, '\1 \2').upcase
75
91
  end
76
92
 
@@ -91,7 +107,6 @@ module RASN1
91
107
  obj
92
108
  end
93
109
 
94
-
95
110
  # @overload initialize(options={})
96
111
  # @param [Hash] options
97
112
  # @option options [Symbol] :class ASN.1 tag class. Default value is +:universal+.
@@ -128,20 +143,10 @@ module RASN1
128
143
  end
129
144
  end
130
145
 
131
- # Used by +#dup+ and +#clone+. Deep copy @value.
132
- def initialize_copy(other)
133
- @value = case
134
- when NilClass, TrueClass, FalseClass, Integer
135
- @value
136
- else
137
- @value.dup
138
- end
139
- @default = case
140
- when NilClass, TrueClass, FalseClass, Integer
141
- @default
142
- else
143
- @default.dup
144
- end
146
+ # Used by +#dup+ and +#clone+. Deep copy @value and @default.
147
+ def initialize_copy(_other)
148
+ @value = @value.dup unless UNDUPPABLE_TYPES.include?(@value.class)
149
+ @default = @default.dup unless UNDUPPABLE_TYPES.include?(@default.class)
145
150
  end
146
151
 
147
152
  # Get value or default value
@@ -192,7 +197,7 @@ module RASN1
192
197
 
193
198
  # @return [::Boolean] +true+ if this is a constructed type
194
199
  def constructed?
195
- !!((self.class < Constructed) || @constructed)
200
+ (self.class < Constructed) || !!@constructed
196
201
  end
197
202
 
198
203
  # Get ASN.1 type
@@ -206,9 +211,9 @@ module RASN1
206
211
  def tag
207
212
  pc = if @constructed.nil?
208
213
  self.class::ASN1_PC
209
- elsif @constructed # true
214
+ elsif @constructed # true
210
215
  Constructed::ASN1_PC
211
- else # false
216
+ else # false
212
217
  0
213
218
  end
214
219
  tag_value | CLASSES[@asn1_class] | pc
@@ -247,11 +252,9 @@ module RASN1
247
252
  # @param [Integer] level
248
253
  # @return [String]
249
254
  def inspect(level=0)
250
- str = ''
251
- str << ' ' * level if level > 0
252
- str << "#{@name} " unless @name.nil?
253
- str << "#{type}: #{value.inspect}"
254
- str << " OPTIONAL" if optional?
255
+ str = common_inspect(level)
256
+ str << " #{value.inspect}"
257
+ str << ' OPTIONAL' if optional?
255
258
  str << " DEFAULT #{@default}" unless @default.nil?
256
259
  str
257
260
  end
@@ -265,6 +268,13 @@ module RASN1
265
268
 
266
269
  private
267
270
 
271
+ def common_inspect(level)
272
+ lvl = level >= 0 ? level : 0
273
+ str = ' ' * lvl
274
+ str << "#{@name} " unless @name.nil?
275
+ str << "#{type}:"
276
+ end
277
+
268
278
  def value_to_der
269
279
  case @value
270
280
  when Base
@@ -274,7 +284,7 @@ module RASN1
274
284
  end
275
285
  end
276
286
 
277
- def der_to_value(der, ber:false)
287
+ def der_to_value(der, ber: false)
278
288
  @value = der
279
289
  end
280
290
 
@@ -292,11 +302,9 @@ module RASN1
292
302
  when nil
293
303
  @asn1_class = :universal
294
304
  when Symbol
295
- if CLASSES.keys.include? asn1_class
296
- @asn1_class = asn1_class
297
- else
298
- raise ClassError
299
- end
305
+ raise ClassError unless CLASSES.keys.include? asn1_class
306
+
307
+ @asn1_class = asn1_class
300
308
  else
301
309
  raise ClassError
302
310
  end
@@ -330,8 +338,8 @@ module RASN1
330
338
  end
331
339
 
332
340
  def build_tag?
333
- !(!@default.nil? and (@value.nil? or @value == @default)) and
334
- !(optional? and @value.nil?)
341
+ !(!@default.nil? && (@value.nil? || (@value == @default))) &&
342
+ !(optional? && @value.nil?)
335
343
  end
336
344
 
337
345
  def build_tag
@@ -356,22 +364,19 @@ module RASN1
356
364
  end
357
365
 
358
366
  def encode_tag
359
- if tag_value <= MAX_TAG
360
- [tag].pack('C')
361
- else
362
- raise ASN1Error, 'multi-byte tag value are not supported'
363
- end
367
+ raise ASN1Error, 'multi-byte tag value are not supported' unless tag_value <= MAX_TAG
368
+
369
+ [tag].pack('C')
364
370
  end
365
371
 
366
372
  def encode_size(size)
367
373
  if size >= INDEFINITE_LENGTH
368
374
  bytes = []
369
375
  while size > 255
370
- bytes << (size & 0xff)
376
+ bytes.unshift(size & 0xff)
371
377
  size >>= 8
372
378
  end
373
- bytes << size
374
- bytes.reverse!
379
+ bytes.unshift(size)
375
380
  bytes.unshift(INDEFINITE_LENGTH | bytes.size)
376
381
  bytes.pack('C*')
377
382
  else
@@ -402,27 +407,25 @@ module RASN1
402
407
  if length == INDEFINITE_LENGTH
403
408
  if primitive?
404
409
  raise ASN1Error, "malformed #{type} TAG: indefinite length " \
405
- "forbidden for primitive types"
410
+ 'forbidden for primitive types'
411
+ elsif ber
412
+ raise NotImplementedError, 'TAG: indefinite length not ' \
413
+ 'supported yet'
406
414
  else
407
- if ber
408
- raise NotImplementedError, "TAG: indefinite length not " \
409
- "supported yet"
410
- else
411
- raise ASN1Error, "TAG: indefinite length forbidden in DER " \
412
- "encoding"
413
- end
415
+ raise ASN1Error, 'TAG: indefinite length forbidden in DER ' \
416
+ 'encoding'
414
417
  end
415
418
  elsif length < INDEFINITE_LENGTH
416
419
  data = der[2, length]
417
420
  else
418
421
  length_length = length & 0x7f
419
- length = der[2, length_length].unpack('C*').
420
- reduce(0) { |len, b| (len << 8) | b }
422
+ length = der[2, length_length].unpack('C*')
423
+ .reduce(0) { |len, b| (len << 8) | b }
421
424
  data = der[2 + length_length, length]
422
425
  end
423
426
 
424
427
  total_length = 2 + length
425
- total_length += length_length if length_length > 0
428
+ total_length += length_length if length_length.positive?
426
429
 
427
430
  [total_length, data]
428
431
  end
@@ -432,14 +435,18 @@ module RASN1
432
435
  end
433
436
 
434
437
  def raise_tag_error(tag)
435
- msg = name.nil? ? '' : "#{name}: "
438
+ msg = name.nil? ? +'' : +"#{name}: "
436
439
  msg << "Expected #{self2name} but get #{tag2name(tag)}"
437
440
  raise ASN1Error, msg
438
441
  end
439
442
 
443
+ def class_from_numeric_tag(tag)
444
+ CLASSES.key(tag & CLASS_MASK)
445
+ end
446
+
440
447
  def self2name
441
- name = CLASSES.key(tag & 0xc0).to_s.upcase
442
- name << " #{tag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
448
+ name = class_from_numeric_tag(tag).to_s.upcase
449
+ name << " #{(tag & Constructed::ASN1_PC).positive? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
443
450
  if implicit? || explicit?
444
451
  name << ' 0x%02X (0x%02X)' % [tag & 0x1f, tag]
445
452
  else
@@ -448,15 +455,15 @@ module RASN1
448
455
  end
449
456
 
450
457
  def tag2name(tag)
451
- return 'no tag' if tag.nil? or tag.empty?
458
+ return 'no tag' if tag.nil? || tag.empty?
452
459
 
453
460
  itag = tag.unpack('C').first
454
- name = CLASSES.key(itag & 0xc0).to_s.upcase
455
- name << " #{itag & Constructed::ASN1_PC > 0 ? 'CONSTRUCTED' : 'PRIMITIVE'}"
456
- type = Types.constants.map { |c| Types.const_get(c) }.
457
- select { |klass| klass < Primitive || klass < Constructed }.
458
- find { |klass| klass::TAG == itag & 0x1f }
459
- name << " #{type.nil? ? "0x%02X (0x%02X)" % [itag & 0x1f, itag] : type.encode_type }"
461
+ name = class_from_numeric_tag(itag).to_s.upcase
462
+ name << " #{(itag & Constructed::ASN1_PC).positive? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
463
+ type = Types.constants.map { |c| Types.const_get(c) }
464
+ .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}"
460
467
  end
461
468
  end
462
469
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Bit String
5
6
  # @author Sylvain Daubert
6
7
  class BitString < Primitive
8
+ # BitString tag value
7
9
  TAG = 0x03
8
10
 
9
11
  # @param [Integer] bit_length
@@ -25,7 +27,7 @@ module RASN1
25
27
  opts = value_or_options.is_a?(Hash) ? value_or_options : options
26
28
  if @default
27
29
  if opts[:bit_length].nil?
28
- raise ASN1Error, "TAG #@name: default bit length is not defined"
30
+ raise ASN1Error, "TAG #{@name}: default bit length is not defined"
29
31
  end
30
32
  @default_bit_length = opts[:bit_length]
31
33
  end
@@ -44,37 +46,33 @@ module RASN1
44
46
  # @param [Integer] level
45
47
  # @return [String]
46
48
  def inspect(level=0)
47
- str = ''
48
- str << ' ' * level if level > 0
49
- str << "#{name} " unless @name.nil?
50
- str << "#{type}: #{value.inspect} (bit length: #{bit_length})"
49
+ str = common_inspect(level)
50
+ str << " #{value.inspect} (bit length: #{bit_length})"
51
51
  end
52
52
 
53
53
  private
54
54
 
55
55
  def build_tag?
56
- !(!@default.nil? and (@value.nil? or @value == @default and
57
- @bit_length == @default_bit_length)) and
58
- !(optional? and @value.nil?)
56
+ !(!@default.nil? && (@value.nil? || (@value == @default) &&
57
+ (@bit_length == @default_bit_length))) &&
58
+ !(optional? && @value.nil?)
59
59
  end
60
60
 
61
61
  def value_to_der
62
- raise ASN1Error, "TAG #@name: bit length is not set" if bit_length.nil?
62
+ raise ASN1Error, "TAG #{@name}: bit length is not set" if bit_length.nil?
63
63
 
64
- while @value.length * 8 < @bit_length
65
- @value << "\x00"
66
- end
64
+ @value << "\x00" while @value.length * 8 < @bit_length
67
65
  @value.force_encoding('BINARY')
68
66
 
69
67
  if @value.length * 8 > @bit_length
70
- max_len = @bit_length / 8 + (@bit_length % 8 > 0 ? 1 : 0)
68
+ max_len = @bit_length / 8 + ((@bit_length % 8).positive? ? 1 : 0)
71
69
  @value = @value[0, max_len]
72
70
  end
73
71
 
74
72
  unused = @value.length * 8 - @bit_length
75
73
  der = [unused, @value].pack('CA*')
76
74
 
77
- if unused > 0
75
+ if unused.positive?
78
76
  last_byte = @value[-1].unpack('C').first
79
77
  last_byte &= (0xff >> unused) << unused
80
78
  der[-1] = [last_byte].pack('C')
@@ -83,7 +81,7 @@ module RASN1
83
81
  der
84
82
  end
85
83
 
86
- def der_to_value(der, ber:false)
84
+ def der_to_value(der, ber: false)
87
85
  unused, @value = der.unpack('CA*')
88
86
  @bit_length = @value.length * 8 - unused
89
87
  end
@@ -1,34 +1,37 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Boolean
5
6
  # @author Sylvain Daubert
6
7
  class Boolean < Primitive
8
+ # Boolean tag value
7
9
  TAG = 0x01
8
10
 
11
+ # @private
12
+ DER_TRUE = 0xff
13
+ # @private
14
+ DER_FALSE = 0
15
+
9
16
  private
10
17
 
11
18
  def value_to_der
12
- [@value ? 0xff : 0x00].pack('C')
19
+ [@value ? DER_TRUE : DER_FALSE].pack('C')
13
20
  end
14
21
 
15
22
  def der_to_value(der, ber: false)
16
- unless der.size == 1
17
- raise ASN1Error, "tag #@name: BOOLEAN should have a length of 1"
18
- end
23
+ raise ASN1Error, "tag #{@name}: BOOLEAN should have a length of 1" unless der.size == 1
19
24
 
20
25
  bool = der.unpack('C').first
21
26
  case bool
22
- when 0
27
+ when DER_FALSE
23
28
  @value = false
24
- when 0xff
29
+ when DER_TRUE
25
30
  @value = true
26
31
  else
27
- if ber
28
- @value = true
29
- else
30
- raise ASN1Error, "tag #@name: bad value 0x%02x for BOOLEAN" % bool
31
- end
32
+ raise ASN1Error, "tag #{@name}: bad value 0x%02x for BOOLEAN" % bool unless ber
33
+
34
+ @value = true
32
35
  end
33
36
  end
34
37
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # A ASN.1 CHOICE is a choice between different types.
5
6
  #
6
7
  # == Create a CHOICE
@@ -31,7 +32,6 @@ module RASN1
31
32
  # choice.chosen_value # => "abc"
32
33
  # @author Sylvain Daubert
33
34
  class Choice < Base
34
-
35
35
  # Chosen type
36
36
  # @return [Integer] index of type in choice value
37
37
  attr_accessor :chosen
@@ -87,19 +87,16 @@ module RASN1
87
87
  next
88
88
  end
89
89
  end
90
- raise ASN1Error, "CHOICE #@name: no type matching #{der.inspect}" unless parsed
90
+ raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
91
91
  end
92
92
 
93
93
  def inspect(level=0)
94
- str = ''
95
- str << ' ' * level if level > 0
96
- str << "#{name} " if name
97
- str << "#{type}:"
98
- if !defined? @chosen
99
- str << ' not chosen!'
100
- else
101
- str << "\n#{@value[@chosen].inspect(level+1)}"
102
- end
94
+ str = common_inspect(level)
95
+ str << if !defined? @chosen
96
+ ' not chosen!'
97
+ else
98
+ "\n#{@value[@chosen].inspect(level + 1)}"
99
+ end
103
100
  end
104
101
 
105
102
  private
@@ -110,5 +107,3 @@ module RASN1
110
107
  end
111
108
  end
112
109
  end
113
-
114
-
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # @abstract This class SHOULD be used as base class for all ASN.1 primitive
5
6
  # types.
6
7
  # Base class for all ASN.1 constructed types
@@ -12,19 +13,17 @@ module RASN1
12
13
  def inspect(level=0)
13
14
  case @value
14
15
  when Array
15
- str = ''
16
- str << ' ' * level if level > 0
17
- str << "#{@name} " unless @name.nil?
18
- level = level.abs
19
- str << "#{type}:\n"
20
- level += 1
16
+ str = common_inspect(level)
17
+ str << "\n"
18
+ level = level.abs + 1
21
19
  @value.each do |item|
22
20
  case item
23
21
  when Base, Model
24
- next if item.optional? and item.value.nil?
22
+ next if item.optional? && item.value.nil?
23
+
25
24
  str << "#{item.inspect(level)}\n"
26
25
  else
27
- str << ' ' * level + item.inspect + "\n"
26
+ str << ' ' * level + item.inspect + "\n"
28
27
  end
29
28
  end
30
29
  str