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.
- checksums.yaml +4 -4
- data/README.md +11 -3
- data/lib/rasn1/model.rb +185 -98
- data/lib/rasn1/types/any.rb +37 -22
- data/lib/rasn1/types/base.rb +217 -171
- data/lib/rasn1/types/bit_string.rb +44 -46
- data/lib/rasn1/types/boolean.rb +22 -14
- data/lib/rasn1/types/choice.rb +11 -16
- data/lib/rasn1/types/constructed.rb +7 -9
- data/lib/rasn1/types/enumerated.rb +10 -15
- data/lib/rasn1/types/generalized_time.rb +105 -64
- data/lib/rasn1/types/ia5string.rb +8 -6
- data/lib/rasn1/types/integer.rb +89 -81
- data/lib/rasn1/types/null.rb +11 -10
- data/lib/rasn1/types/numeric_string.rb +7 -7
- data/lib/rasn1/types/object_id.rb +19 -49
- data/lib/rasn1/types/octet_string.rb +6 -6
- data/lib/rasn1/types/primitive.rb +2 -1
- data/lib/rasn1/types/printable_string.rb +8 -7
- data/lib/rasn1/types/sequence.rb +39 -8
- data/lib/rasn1/types/sequence_of.rb +55 -41
- data/lib/rasn1/types/set.rb +4 -2
- data/lib/rasn1/types/set_of.rb +4 -3
- data/lib/rasn1/types/utc_time.rb +7 -5
- data/lib/rasn1/types/utf8_string.rb +7 -5
- data/lib/rasn1/types/visible_string.rb +4 -3
- data/lib/rasn1/types.rb +54 -24
- data/lib/rasn1/version.rb +3 -1
- data/lib/rasn1.rb +8 -7
- metadata +27 -51
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -28
- data/.travis.yml +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -12
- data/rasn1.gemspec +0 -32
data/lib/rasn1/types/integer.rb
CHANGED
@@ -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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
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(
|
18
|
+
def initialize(options={})
|
23
19
|
super
|
24
|
-
|
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 [
|
51
|
-
def value=(
|
52
|
-
|
53
|
-
|
54
|
-
|
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
|
-
|
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
|
-
@
|
33
|
+
@no_value = true
|
34
|
+
@value = void_value
|
70
35
|
else
|
71
|
-
raise EnumeratedError, "
|
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.
|
79
|
-
@value
|
48
|
+
if @enum.empty?
|
49
|
+
value? ? @value : @default || 0
|
80
50
|
else
|
81
|
-
@enum[@value
|
51
|
+
@enum[value? ? @value : @default] || 0
|
82
52
|
end
|
83
53
|
end
|
84
54
|
|
85
55
|
private
|
86
56
|
|
87
|
-
def
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
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
|
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, "
|
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.
|
133
|
+
return if @enum.empty?
|
127
134
|
|
135
|
+
int_value = @value
|
128
136
|
@value = @enum.key(@value)
|
129
|
-
raise EnumeratedError, "
|
137
|
+
raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless value?
|
130
138
|
end
|
131
139
|
|
132
140
|
def explicit_type
|
data/lib/rasn1/types/null.rb
CHANGED
@@ -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
|
-
|
8
|
+
# Null id value
|
9
|
+
ID = 0x05
|
8
10
|
|
9
11
|
# @return [String]
|
10
12
|
def inspect(level=0)
|
11
|
-
str = ''
|
12
|
-
str << '
|
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,
|
27
|
-
|
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
|
-
|
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
|
-
|
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!
|
14
|
+
ids = @value.to_s.split('.').map!(&:to_i)
|
13
15
|
|
14
|
-
if ids[0] > 2
|
15
|
-
|
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
|
-
|
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
|
-
|
61
|
-
|
31
|
+
ids = []
|
32
|
+
current_id = 0
|
62
33
|
bytes.each do |byte|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
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
|
-
|
15
|
+
# OctetString id value
|
16
|
+
ID = 4
|
15
17
|
|
16
18
|
def inspect(level=0)
|
17
|
-
str =
|
18
|
-
str <<
|
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,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
|
-
|
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
|
-
|
29
|
-
|
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
|
data/lib/rasn1/types/sequence.rb
CHANGED
@@ -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
|
-
|
29
|
+
# Sequence id value
|
30
|
+
ID = 0x10
|
29
31
|
|
30
32
|
# @see Base#initialize
|
31
|
-
def initialize(
|
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
|
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
|
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)
|
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
|