rasn1 0.6.6 → 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.
@@ -1,10 +1,12 @@
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
7
- TAG = 0x03
8
+ # BitString id value
9
+ ID = 3
8
10
 
9
11
  # @param [Integer] bit_length
10
12
  # @return [Integer]
@@ -22,14 +24,12 @@ module RASN1
22
24
  # @see Base#initialize common options to all ASN.1 types
23
25
  def initialize(value_or_options={}, options={})
24
26
  super
25
- opts = value_or_options.is_a?(Hash) ? value_or_options : options
26
27
  if @default
27
- if opts[:bit_length].nil?
28
- raise ASN1Error, "TAG #@name: default bit length is not defined"
29
- end
30
- @default_bit_length = opts[:bit_length]
28
+ raise ASN1Error, "#{@name}: default bit length is not defined" if @options[:bit_length].nil?
29
+
30
+ @default_bit_length = @options[:bit_length]
31
31
  end
32
- @bit_length = opts[:bit_length]
32
+ @bit_length = @options[:bit_length]
33
33
  end
34
34
 
35
35
  # Get bit length
@@ -44,38 +44,34 @@ module RASN1
44
44
  # @param [Integer] level
45
45
  # @return [String]
46
46
  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})"
47
+ str = common_inspect(level)
48
+ str << " #{value.inspect} (bit length: #{bit_length})"
51
49
  end
52
50
 
53
51
  private
54
52
 
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?)
53
+ def can_build?
54
+ !(!@default.nil? && (@value.nil? || (@value == @default) &&
55
+ (@bit_length == @default_bit_length))) &&
56
+ !(optional? && @value.nil?)
59
57
  end
60
58
 
61
59
  def value_to_der
62
- raise ASN1Error, "TAG #@name: bit length is not set" if bit_length.nil?
60
+ raise ASN1Error, "#{@name}: bit length is not set" if bit_length.nil?
63
61
 
64
- while @value.length * 8 < @bit_length
65
- @value << "\x00"
66
- end
62
+ @value << "\x00" while @value.length * 8 < @bit_length
67
63
  @value.force_encoding('BINARY')
68
64
 
69
65
  if @value.length * 8 > @bit_length
70
- max_len = @bit_length / 8 + (@bit_length % 8 > 0 ? 1 : 0)
66
+ max_len = @bit_length / 8 + ((@bit_length % 8).positive? ? 1 : 0)
71
67
  @value = @value[0, max_len]
72
68
  end
73
69
 
74
70
  unused = @value.length * 8 - @bit_length
75
71
  der = [unused, @value].pack('CA*')
76
72
 
77
- if unused > 0
78
- last_byte = @value[-1].unpack('C').first
73
+ if unused.positive?
74
+ last_byte = @value[-1].unpack1('C')
79
75
  last_byte &= (0xff >> unused) << unused
80
76
  der[-1] = [last_byte].pack('C')
81
77
  end
@@ -83,7 +79,7 @@ module RASN1
83
79
  der
84
80
  end
85
81
 
86
- def der_to_value(der, ber:false)
82
+ def der_to_value(der, ber: false)
87
83
  unused, @value = der.unpack('CA*')
88
84
  @bit_length = @value.length * 8 - unused
89
85
  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
7
- TAG = 0x01
8
+ # Boolean id value
9
+ ID = 0x01
10
+
11
+ # @private
12
+ DER_TRUE = 0xff
13
+ # @private
14
+ DER_FALSE = 0
8
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
- bool = der.unpack('C').first
25
+ bool = der.unpack1('C')
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
@@ -79,7 +79,7 @@ module RASN1
79
79
  @value.each_with_index do |element, i|
80
80
  begin
81
81
  @chosen = i
82
- nb_bytes = element.parse!(der)
82
+ nb_bytes = element.parse!(der, ber: ber)
83
83
  parsed = true
84
84
  return nb_bytes
85
85
  rescue ASN1Error
@@ -87,28 +87,23 @@ 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 @chosen.nil?
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
106
103
 
107
104
  def check_chosen
108
- raise ChoiceError if @chosen.nil?
105
+ raise ChoiceError if !defined?(@chosen) || @chosen.nil?
109
106
  end
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,18 @@ 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
27
+ str << "#{item.inspect}\n"
28
28
  end
29
29
  end
30
30
  str
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Enumerated
5
6
  #
6
7
  # An enumerated type permits to assign names to integer values. It may be defined
@@ -21,7 +22,8 @@ module RASN1
21
22
  # @!attribute enum
22
23
  # @return [Hash]
23
24
 
24
- TAG = 0x0a
25
+ # Enumerated id value
26
+ ID = 0x0a
25
27
 
26
28
  # @overload initialize(options={})
27
29
  # @option options [Hash] :enum enumeration hash. Keys are names, and values
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 GeneralizedTime
5
6
  #
6
7
  # +{#value} of a {GeneralizedTime} should be a ruby Time.
@@ -20,7 +21,8 @@ module RASN1
20
21
  # second are supported.
21
22
  # @author Sylvain Daubert
22
23
  class GeneralizedTime < Primitive
23
- TAG = 24
24
+ # GeneralizedTime id value
25
+ ID = 24
24
26
 
25
27
  # Get ASN.1 type
26
28
  # @return [String]
@@ -29,50 +31,48 @@ module RASN1
29
31
  end
30
32
 
31
33
  private
32
-
34
+
33
35
  def value_to_der
34
- if @value.nsec > 0
36
+ if @value.nsec.positive?
35
37
  der = @value.getutc.strftime('%Y%m%d%H%M%S.%9NZ')
36
38
  der.sub(/0+Z/, 'Z')
37
39
  else
38
40
  @value.getutc.strftime('%Y%m%d%H%M%SZ')
39
41
  end
40
42
  end
41
-
43
+
42
44
  def der_to_value(der, ber: false)
43
45
  date_hour, fraction = der.split('.')
44
46
  utc_offset_forced = false
45
47
  if fraction.nil?
46
- if date_hour[-1] != 'Z' and date_hour !~ /[+-]\d+$/
48
+ if (date_hour[-1] != 'Z') && (date_hour !~ /[+-]\d+$/)
47
49
  # If not UTC, have to add offset with UTC to force
48
50
  # DateTime#strptime to generate a local time. But this difference
49
51
  # may be errored because of DST.
50
52
  date_hour << Time.now.strftime('%z')
51
53
  utc_offset_forced = true
52
54
  end
55
+ elsif fraction[-1] == 'Z'
56
+ fraction = fraction[0...-1]
57
+ date_hour << 'Z'
53
58
  else
54
- if fraction[-1] == 'Z'
55
- fraction = fraction[0...-1]
56
- date_hour << 'Z'
59
+ match = fraction.match(/(\d+)([+-]\d+)/)
60
+ if match
61
+ # fraction contains fraction and timezone info. Split them
62
+ fraction = match[1]
63
+ date_hour << match[2]
57
64
  else
58
- match = fraction.match(/(\d+)([+-]\d+)/)
59
- if match
60
- # fraction contains fraction and timezone info. Split them
61
- fraction = match[1]
62
- date_hour << match[2]
63
- else
64
- # fraction only contains fraction.
65
- # Have to add offset with UTC to force DateTime#strptime to
66
- # generate a local time. But this difference may be errored
67
- # because of DST.
68
- date_hour << Time.now.strftime('%z')
69
- utc_offset_forced = true
70
- end
65
+ # fraction only contains fraction.
66
+ # Have to add offset with UTC to force DateTime#strptime to
67
+ # generate a local time. But this difference may be errored
68
+ # because of DST.
69
+ date_hour << Time.now.strftime('%z')
70
+ utc_offset_forced = true
71
71
  end
72
72
  end
73
73
  format = case date_hour.size
74
74
  when 11
75
- frac_base = 60*60
75
+ frac_base = 60 * 60
76
76
  '%Y%m%d%HZ'
77
77
  when 13
78
78
  frac_base = 60
@@ -100,9 +100,7 @@ module RASN1
100
100
  # Check DST. There may be a shift of one hour...
101
101
  if utc_offset_forced
102
102
  compare_time = Time.new(*@value.to_a[0..5].reverse)
103
- if compare_time.utc_offset != @value.utc_offset
104
- @value = compare_time
105
- end
103
+ @value = compare_time if compare_time.utc_offset != @value.utc_offset
106
104
  end
107
105
  @value += ".#{fraction}".to_r * frac_base unless fraction.nil?
108
106
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 IA5 String
5
6
  # @author Sylvain Daubert
6
7
  class IA5String < OctetString
7
- TAG = 22
8
+ # IA5String id value
9
+ ID = 22
8
10
 
9
11
  # Get ASN.1 type
10
12
  # @return [String]
@@ -13,15 +15,15 @@ module RASN1
13
15
  end
14
16
 
15
17
  private
16
-
18
+
17
19
  def value_to_der
18
20
  @value.to_s.force_encoding('US-ASCII').force_encoding('BINARY')
19
21
  end
20
22
 
21
- def der_to_value(der, ber:false)
23
+ def der_to_value(der, ber: false)
22
24
  super
23
25
  @value.force_encoding('US-ASCII')
24
26
  end
25
- end
27
+ end
26
28
  end
27
29
  end
@@ -1,13 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Integer
5
6
  # @author Sylvain Daubert
6
7
  class Integer < Primitive
7
8
  # @return [Hash,nil]
8
9
  attr_reader :enum
9
10
 
10
- TAG = 0x02
11
+ # Integer id value
12
+ ID = 2
11
13
 
12
14
  # @overload initialize(options={})
13
15
  # @option options [Hash] :enum enumeration hash. Keys are names, and values
@@ -21,8 +23,7 @@ module RASN1
21
23
  # @see Base#initialize common options to all ASN.1 types
22
24
  def initialize(value_or_options={}, options={})
23
25
  super
24
- opts = value_or_options.is_a?(Hash) ? value_or_options : options
25
- @enum = opts[:enum]
26
+ @enum = @options[:enum]
26
27
 
27
28
  return if @enum.nil?
28
29
 
@@ -30,45 +31,39 @@ module RASN1
30
31
  self.value = @value
31
32
 
32
33
  case @default
33
- when String,Symbol
34
- unless @enum.has_key? @default
35
- raise EnumeratedError, "TAG #@name: unknwon enumerated default value #@default"
36
- end
34
+ when String, Symbol
35
+ raise EnumeratedError, "#{@name}: unknwon enumerated default value #@{default}" unless @enum.key? @default
37
36
  when ::Integer
38
- if @enum.has_value? @default
39
- @default = @enum.key(@default)
40
- else
41
- raise EnumeratedError, "TAG #@name: default value #@default not in enumeration"
42
- end
37
+ raise EnumeratedError, "#{@name}: default value #@{default} not in enumeration" unless @enum.value? @default
38
+
39
+ @default = @enum.key(@default)
43
40
  when nil
44
41
  else
45
- raise TypeError, "TAG #@name: #{@default.class} not handled as default value"
42
+ raise TypeError, "#{@name}: #{@default.class} not handled as default value"
46
43
  end
47
44
  end
48
45
 
49
46
  # @param [Integer,String,Symbol,nil] v
50
47
  # @return [String,Symbol,nil]
51
- def value=(v)
52
- case v
53
- when String,Symbol
54
- raise EnumeratedError, 'TAG #@name has no :enum' if @enum.nil?
48
+ def value=(val)
49
+ case val
50
+ when String, Symbol
51
+ raise EnumeratedError, "#{@name} has no :enum" if @enum.nil?
52
+ raise EnumeratedError, "#{@name}: unknwon enumerated value #{val}" unless @enum.key? val
55
53
 
56
- unless @enum.has_key? v
57
- raise EnumeratedError, "TAG #@name: unknwon enumerated value #{v}"
58
- end
59
- @value = v
54
+ @value = val
60
55
  when ::Integer
61
56
  if @enum.nil?
62
- @value = v
63
- elsif @enum.has_value? v
64
- @value = @enum.key(v)
57
+ @value = val
58
+ elsif @enum.value? val
59
+ @value = @enum.key(val)
65
60
  else
66
- raise EnumeratedError, "TAG #@name: #{v} not in enumeration"
61
+ raise EnumeratedError, "#{@name}: #{val} not in enumeration"
67
62
  end
68
63
  when nil
69
64
  @value = nil
70
65
  else
71
- raise EnumeratedError, "TAG #@name: not in enumeration"
66
+ raise EnumeratedError, "#{@name}: not in enumeration"
72
67
  end
73
68
  end
74
69
 
@@ -86,18 +81,12 @@ module RASN1
86
81
 
87
82
  def int_value_to_der(value=nil)
88
83
  v = value || @value
89
- size = v.bit_length / 8 + (v.bit_length % 8 > 0 ? 1 : 0)
90
- size = 1 if size == 0
91
- comp_value = if v > 0
92
- # If MSB is 1, increment size to set initial octet
93
- # to 0 to amrk it as a positive integer
94
- size += 1 if v >> (size * 8 - 1) == 1
95
- v
96
- else
97
- ~(v.abs) + 1
98
- end
99
- ary = []
100
- size.times { ary << (comp_value & 0xff); comp_value >>= 8 }
84
+ size = v.bit_length / 8 + ((v.bit_length % 8).positive? ? 1 : 0)
85
+ size = 1 if size.zero?
86
+ comp_value = v >= 0 ? v : (~(-v) + 1) & ((1 << (size * 8)) - 1)
87
+ ary = comp_value.digits(256)
88
+ # v is > 0 and its MSBit is 1. Add a 0 byte to mark it as positive
89
+ ary << 0 if v.positive? && (v >> (size * 8 - 1) == 1)
101
90
  ary.reverse.pack('C*')
102
91
  end
103
92
 
@@ -108,16 +97,14 @@ module RASN1
108
97
  when ::Integer
109
98
  int_value_to_der
110
99
  else
111
- raise TypeError, "TAG #@name: #{@value.class} not handled"
100
+ raise TypeError, "#{@name}: #{@value.class} not handled"
112
101
  end
113
102
  end
114
103
 
115
104
  def der_to_int_value(der, ber: false)
116
105
  ary = der.unpack('C*')
117
106
  v = ary.reduce(0) { |len, b| (len << 8) | b }
118
- if ary[0] & 0x80 == 0x80
119
- v = -((~v & ((1 << v.bit_length) - 1)) + 1)
120
- end
107
+ v = -((~v & ((1 << v.bit_length) - 1)) + 1) if ary[0] & 0x80 == 0x80
121
108
  v
122
109
  end
123
110
 
@@ -126,11 +113,11 @@ module RASN1
126
113
  return if @enum.nil?
127
114
 
128
115
  @value = @enum.key(@value)
129
- raise EnumeratedError, "TAG #@name: value #{v} not in enumeration" if @value.nil?
116
+ raise EnumeratedError, "#{@name}: value #{v} not in enumeration" if @value.nil?
130
117
  end
131
118
 
132
119
  def explicit_type
133
- @enum.nil? ? self.class.new(@name) : self.class.new(@name, enum: @enum)
120
+ self.class.new(name: @name, enum: @enum)
134
121
  end
135
122
  end
136
123
  end