rasn1 0.6.8 → 0.9.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,132 +1,140 @@
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
-
12
- # @overload initialize(options={})
13
- # @option options [Hash] :enum enumeration hash. Keys are names, and values
14
- # are integers.
15
- # @raise [EnumeratedError] +:default+ value is unknown when +:enum+ key is present
16
- # @overload initialize(value, options={})
17
- # @param [Object] value value to set for this ASN.1 object
18
- # @option options [Hash] :enum enumeration hash. Keys are names, and values
19
- # are integers. This key is mandatory.
20
- # @raise [EnumeratedError] +:default+ value is unknown when +:enum+ key is present
11
+ # Integer id value
12
+ ID = 2
13
+
14
+ # @option options [Hash] :enum enumeration hash. Keys are names, and values
15
+ # are integers.
16
+ # @raise [EnumeratedError] +:default+ value is unknown when +:enum+ key is present
21
17
  # @see Base#initialize common options to all ASN.1 types
22
- def initialize(value_or_options={}, options={})
18
+ def initialize(options={})
23
19
  super
24
- opts = value_or_options.is_a?(Hash) ? value_or_options : options
25
- @enum = opts[:enum]
26
-
27
- return if @enum.nil?
28
-
29
- # To ensure @value has the correct type
30
- self.value = @value
31
-
32
- case @default
33
- when String,Symbol
34
- unless @enum.has_key? @default
35
- raise EnumeratedError, "TAG #@name: unknwon enumerated default value #@default"
36
- end
37
- 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
43
- when nil
44
- else
45
- raise TypeError, "TAG #@name: #{@default.class} not handled as default value"
46
- end
20
+ initialize_enum(@options[:enum])
47
21
  end
48
22
 
49
23
  # @param [Integer,String,Symbol,nil] v
50
- # @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?
55
-
56
- unless @enum.has_key? v
57
- raise EnumeratedError, "TAG #@name: unknwon enumerated value #{v}"
58
- end
59
- @value = v
24
+ # @return [void]
25
+ def value=(val)
26
+ @no_value = false
27
+ case val
28
+ when String, Symbol
29
+ value_from_string_or_symbol(val)
60
30
  when ::Integer
61
- if @enum.nil?
62
- @value = v
63
- elsif @enum.has_value? v
64
- @value = @enum.key(v)
65
- else
66
- raise EnumeratedError, "TAG #@name: #{v} not in enumeration"
67
- end
31
+ value_from_integer(val)
68
32
  when nil
69
- @value = nil
33
+ @no_value = true
34
+ @value = void_value
70
35
  else
71
- raise EnumeratedError, "TAG #@name: not in enumeration"
36
+ raise EnumeratedError, "#{@name}: not in enumeration"
72
37
  end
73
38
  end
74
39
 
40
+ # @return [Integer]
41
+ def void_value
42
+ 0
43
+ end
44
+
75
45
  # Integer value
76
46
  # @return [Integer]
77
47
  def to_i
78
- if @enum.nil?
79
- @value || @default || 0
48
+ if @enum.empty?
49
+ value? ? @value : @default || 0
80
50
  else
81
- @enum[@value || @default] || 0
51
+ @enum[value? ? @value : @default] || 0
82
52
  end
83
53
  end
84
54
 
85
55
  private
86
56
 
87
- def int_value_to_der(value=nil)
88
- 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 }
57
+ def initialize_enum(enum)
58
+ @enum = enum || {}
59
+ return if @enum.empty?
60
+
61
+ # To ensure @value has the correct type
62
+ self.value = @value if value?
63
+
64
+ check_enum_default
65
+ end
66
+
67
+ def check_enum_default
68
+ case @default
69
+ when String, Symbol
70
+ raise EnumeratedError, "#{@name}: unknwon enumerated default value #@{default}" unless @enum.key?(@default)
71
+ when ::Integer
72
+ raise EnumeratedError, "#{@name}: default value #@{default} not in enumeration" unless @enum.value?(@default)
73
+
74
+ @default = @enum.key(@default)
75
+ when nil
76
+ else
77
+ raise TypeError, "#{@name}: #{@default.class} not handled as default value"
78
+ end
79
+ end
80
+
81
+ def value_from_string_or_symbol(val)
82
+ raise EnumeratedError, "#{@name} has no :enum" if @enum.empty?
83
+ raise EnumeratedError, "#{@name}: unknwon enumerated value #{val}" unless @enum.key? val
84
+
85
+ @value = val
86
+ end
87
+
88
+ def value_from_integer(val)
89
+ if @enum.empty?
90
+ @value = val
91
+ elsif @enum.value? val
92
+ @value = @enum.key(val)
93
+ else
94
+ raise EnumeratedError, "#{@name}: #{val} not in enumeration"
95
+ end
96
+ end
97
+
98
+ def int_value_to_der(value)
99
+ size = compute_size(value)
100
+ comp_value = value >= 0 ? value : (~(-value) + 1) & ((1 << (size * 8)) - 1)
101
+ ary = comp_value.digits(256)
102
+ # v is > 0 and its MSBit is 1. Add a 0 byte to mark it as positive
103
+ ary << 0 if value.positive? && (value >> (size * 8 - 1) == 1)
101
104
  ary.reverse.pack('C*')
102
105
  end
103
106
 
107
+ def compute_size(value)
108
+ size = value.bit_length / 8 + ((value.bit_length % 8).positive? ? 1 : 0)
109
+ size = 1 if size.zero?
110
+ size
111
+ end
112
+
104
113
  def value_to_der
105
114
  case @value
106
115
  when String, Symbol
107
- int_value_to_der @enum[@value]
116
+ int_value_to_der(@enum[@value])
108
117
  when ::Integer
109
- int_value_to_der
118
+ int_value_to_der(@value)
110
119
  else
111
- raise TypeError, "TAG #@name: #{@value.class} not handled"
120
+ raise TypeError, "#{@name}: #{@value.class} not handled"
112
121
  end
113
122
  end
114
123
 
115
- def der_to_int_value(der, ber: false)
124
+ def der_to_int_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
116
125
  ary = der.unpack('C*')
117
126
  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
127
+ v = -((~v & ((1 << v.bit_length) - 1)) + 1) if ary[0] & 0x80 == 0x80
121
128
  v
122
129
  end
123
130
 
124
131
  def der_to_value(der, ber: false)
125
132
  @value = der_to_int_value(der, ber: ber)
126
- return if @enum.nil?
133
+ return if @enum.empty?
127
134
 
135
+ int_value = @value
128
136
  @value = @enum.key(@value)
129
- raise EnumeratedError, "TAG #@name: value #{v} not in enumeration" if @value.nil?
137
+ raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless value?
130
138
  end
131
139
 
132
140
  def explicit_type
@@ -1,18 +1,17 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Null
5
6
  # @author Sylvain Daubert
6
7
  class Null < Primitive
7
- TAG = 0x05
8
+ # Null id value
9
+ ID = 0x05
8
10
 
9
11
  # @return [String]
10
12
  def inspect(level=0)
11
- str = ''
12
- str << ' ' * level if level > 0
13
- str << "#{@name} " unless @name.nil?
14
- str << "#{type}"
15
- str << " OPTIONAL" if optional?
13
+ str = common_inspect(level)[0..-2] # remove terminal ':'
14
+ str << ' OPTIONAL' if optional?
16
15
  str
17
16
  end
18
17
 
@@ -22,9 +21,11 @@ module RASN1
22
21
  ''
23
22
  end
24
23
 
25
- def der_to_value(der, ber: false)
26
- raise ASN1Error, "NULL TAG should not have content!" if der.length > 0
27
- @value = nil
24
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
25
+ raise ASN1Error, 'NULL should not have content!' if der.length.positive?
26
+
27
+ @no_value = true
28
+ @value = void_value
28
29
  end
29
30
  end
30
31
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Numeric String
5
6
  # @author Sylvain Daubert
6
7
  class NumericString < OctetString
7
- TAG = 18
8
+ # NumericString id value
9
+ ID = 18
8
10
 
9
11
  # Get ASN.1 type
10
12
  # @return [String]
@@ -13,21 +15,19 @@ module RASN1
13
15
  end
14
16
 
15
17
  private
16
-
18
+
17
19
  def value_to_der
18
20
  check_characters
19
21
  @value.to_s.force_encoding('BINARY')
20
22
  end
21
23
 
22
- def der_to_value(der, ber:false)
24
+ def der_to_value(der, ber: false)
23
25
  super
24
26
  check_characters
25
27
  end
26
28
 
27
29
  def check_characters
28
- if @value.to_s =~ /([^0-9 ])/
29
- raise ASN1Error, "NUMERIC STRING #@name: invalid character: '#{$1}'"
30
- end
30
+ raise ASN1Error, "NUMERIC STRING #{@name}: invalid character: '#{$1}'" if @value.to_s =~ /([^0-9 ])/
31
31
  end
32
32
  end
33
33
  end
@@ -1,77 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Object ID
5
6
  # @author Sylvain Daubert
6
7
  class ObjectId < Primitive
7
- TAG = 6
8
+ # ObjectId id value
9
+ ID = 6
8
10
 
9
11
  private
10
12
 
11
13
  def value_to_der
12
- ids = @value.split('.').map! { |str| str.to_i }
14
+ ids = @value.to_s.split('.').map!(&:to_i)
13
15
 
14
- if ids[0] > 2
15
- raise ASN1Error, 'OBJECT ID #@name: first subidentifier should be less than 3'
16
- end
17
- if ids[0] < 2 and ids[1] > 39
18
- raise ASN1Error, 'OBJECT ID #@name: second subidentifier should be less than 40'
19
- end
16
+ raise ASN1Error, "OBJECT ID #{@name}: first subidentifier should be less than 3" if ids[0] > 2
17
+ raise ASN1Error, "OBJECT ID #{@name}: second subidentifier should be less than 40" if (ids[0] < 2) && (ids[1] > 39)
20
18
 
21
19
  ids[0, 2] = ids[0] * 40 + ids[1]
22
20
  ids.map! do |v|
23
21
  next v if v < 128
24
22
 
25
- ary = []
26
- while v > 0
27
- ary.unshift (v & 0x7f) | 0x80
28
- v >>= 7
29
- end
30
- ary[-1] &= 0x7f
31
- ary
23
+ unsigned_to_chained_octets(v)
32
24
  end
33
25
  ids.flatten.pack('C*')
34
26
  end
35
27
 
36
- def der_to_value(der, ber: false)
28
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
37
29
  bytes = der.unpack('C*')
38
- ids = if bytes[0] < 80
39
- remove = 1
40
- [ bytes[0] / 40, bytes[0] % 40]
41
- elsif bytes[0] < 128
42
- remove = 1
43
- [2, bytes[0] - 80]
44
- else
45
- remove = 1
46
- second_id = bytes[0] & 0x7f
47
- bytes[1..-1].each do |byte|
48
- remove += 1
49
- second_id <<= 7
50
- if byte < 128
51
- second_id |= byte
52
- break
53
- else
54
- second_id |= byte & 0x7f
55
- end
56
- end
57
- [2, second_id - 80]
58
- end
59
30
 
60
- id = 0
61
- bytes.shift(remove)
31
+ ids = []
32
+ current_id = 0
62
33
  bytes.each do |byte|
63
- if byte < 128
64
- if id == 0
65
- ids << byte
66
- else
67
- ids << ((id << 7) | byte)
68
- id = 0
69
- end
70
- else
71
- id = (id << 7) | (byte & 0x7f)
34
+ current_id = (current_id << 7) | (byte & 0x7f)
35
+ if (byte & 0x80).zero?
36
+ ids << current_id
37
+ current_id = 0
72
38
  end
73
39
  end
74
40
 
41
+ first_id = [2, ids.first / 40].min
42
+ second_id = ids.first - first_id * 40
43
+ ids[0..0] = [first_id, second_id]
44
+
75
45
  @value = ids.join('.')
76
46
  end
77
47
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Octet String
5
6
  #
6
7
  # An OCTET STRINT may contain another primtive object:
@@ -11,13 +12,12 @@ module RASN1
11
12
  # os.to_der # => DER string with INTEGER in OCTET STRING
12
13
  # @author Sylvain Daubert
13
14
  class OctetString < Primitive
14
- TAG = 0x04
15
+ # OctetString id value
16
+ ID = 4
15
17
 
16
18
  def inspect(level=0)
17
- str = ''
18
- str << ' ' * level if level > 0
19
- str << "#{@name} " unless @name.nil?
20
- str << "#{type}: #{value.inspect}"
19
+ str = common_inspect(level)
20
+ str << " #{value.inspect}"
21
21
  end
22
22
  end
23
23
  end
@@ -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
  # @author Sylvain Daubert
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 Printable String
5
6
  # @author Sylvain Daubert
6
7
  class PrintableString < OctetString
7
- TAG = 19
8
+ # PrintableString id value
9
+ ID = 19
8
10
 
9
11
  # Get ASN.1 type
10
12
  # @return [String]
@@ -13,21 +15,20 @@ module RASN1
13
15
  end
14
16
 
15
17
  private
16
-
18
+
17
19
  def value_to_der
18
20
  check_characters
19
21
  @value.to_s.force_encoding('BINARY')
20
22
  end
21
23
 
22
- def der_to_value(der, ber:false)
24
+ def der_to_value(der, ber: false)
23
25
  super
24
26
  check_characters
25
27
  end
26
28
 
27
29
  def check_characters
28
- if @value.to_s =~ /([^a-zA-Z0-9 '=\(\)\+,\-\.\/:\?])/
29
- raise ASN1Error, "PRINTABLE STRING #@name: invalid character: '#{$1}'"
30
- end
30
+ m = @value.to_s.match(%r{([^a-zA-Z0-9 '=()+,\-./:?])})
31
+ raise ASN1Error, "PRINTABLE STRING #{@name}: invalid character: '#{m[1]}'" if m
31
32
  end
32
33
  end
33
34
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module RASN1
2
4
  module Types
3
-
4
5
  # ASN.1 sequence
5
6
  #
6
7
  # A sequence is a collection of another ASN.1 types.
@@ -25,17 +26,43 @@ module RASN1
25
26
  # seq.value # => String
26
27
  # @author Sylvain Daubert
27
28
  class Sequence < Constructed
28
- TAG = 0x10
29
+ # Sequence id value
30
+ ID = 0x10
29
31
 
30
32
  # @see Base#initialize
31
- def initialize(value_or_options={}, options={})
33
+ def initialize(options={})
32
34
  super
35
+ @no_value = false
33
36
  @value ||= []
34
37
  end
35
38
 
36
39
  def initialize_copy(other)
37
40
  super
38
- @value = @value.map { |v| v.dup }
41
+ @value = case @value
42
+ when Array
43
+ @value.map(&:dup)
44
+ else
45
+ @value.dup
46
+ end
47
+ end
48
+
49
+ # @return [Array]
50
+ def void_value
51
+ []
52
+ end
53
+
54
+ # Get element at index +idx+, or element of name +name+
55
+ # @param [Integer, String, Symbol] idx_or_name
56
+ # @return [Object,nil]
57
+ def [](idx_or_name)
58
+ return unless @value.is_a?(Array)
59
+
60
+ case idx_or_name
61
+ when Integer
62
+ @value[idx_or_name.to_i]
63
+ when String, Symbol
64
+ @value.find { |elt| elt.name == idx_or_name }
65
+ end
39
66
  end
40
67
 
41
68
  private
@@ -43,14 +70,14 @@ module RASN1
43
70
  def value_to_der
44
71
  case @value
45
72
  when Array
46
- @value.map { |element| element.to_der }.join
73
+ @value.map(&:to_der).join
47
74
  else
48
75
  @value.to_s
49
76
  end
50
77
  end
51
78
 
52
- def der_to_value(der, ber:false)
53
- if @value.is_a?(Array) and !@value.empty?
79
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
80
+ if @value.is_a?(Array) && !@value.empty?
54
81
  nb_bytes = 0
55
82
  @value.each do |element|
56
83
  nb_bytes += element.parse!(der[nb_bytes..-1])
@@ -58,7 +85,11 @@ module RASN1
58
85
  else
59
86
  @value = der
60
87
  der.length
61
- end
88
+ end
89
+ end
90
+
91
+ def explicit_type
92
+ self.class.new(value: @value)
62
93
  end
63
94
  end
64
95
  end