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.
- checksums.yaml +4 -4
- data/README.md +11 -2
- data/lib/rasn1.rb +7 -6
- data/lib/rasn1/model.rb +108 -59
- data/lib/rasn1/types.rb +49 -20
- data/lib/rasn1/types/any.rb +26 -16
- data/lib/rasn1/types/base.rb +146 -115
- data/lib/rasn1/types/bit_string.rb +20 -24
- data/lib/rasn1/types/boolean.rb +17 -14
- data/lib/rasn1/types/choice.rb +11 -16
- data/lib/rasn1/types/constructed.rb +9 -9
- data/lib/rasn1/types/enumerated.rb +4 -2
- data/lib/rasn1/types/generalized_time.rb +24 -26
- data/lib/rasn1/types/ia5string.rb +7 -5
- data/lib/rasn1/types/integer.rb +32 -45
- data/lib/rasn1/types/null.rb +8 -8
- data/lib/rasn1/types/numeric_string.rb +7 -7
- data/lib/rasn1/types/object_id.rb +18 -48
- 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 +21 -7
- data/lib/rasn1/types/sequence_of.rb +15 -13
- data/lib/rasn1/types/set.rb +4 -2
- data/lib/rasn1/types/set_of.rb +4 -3
- data/lib/rasn1/types/utc_time.rb +6 -4
- data/lib/rasn1/types/utf8_string.rb +6 -4
- data/lib/rasn1/types/visible_string.rb +4 -3
- data/lib/rasn1/version.rb +3 -1
- metadata +27 -50
- data/.gitignore +0 -11
- data/.rubocop.yml +0 -29
- data/.travis.yml +0 -9
- data/Gemfile +0 -4
- data/Rakefile +0 -12
- data/rasn1.gemspec +0 -32
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
|
|
@@ -23,7 +22,8 @@ module RASN1
|
|
23
22
|
end
|
24
23
|
|
25
24
|
def der_to_value(der, ber: false)
|
26
|
-
raise ASN1Error,
|
25
|
+
raise ASN1Error, 'NULL should not have content!' if der.length.positive?
|
26
|
+
|
27
27
|
@value = nil
|
28
28
|
end
|
29
29
|
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.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
28
|
def der_to_value(der, ber: false)
|
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,7 +26,8 @@ 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
33
|
def initialize(value_or_options={}, options={})
|
@@ -35,7 +37,19 @@ module RASN1
|
|
35
37
|
|
36
38
|
def initialize_copy(other)
|
37
39
|
super
|
38
|
-
@value = @value.map
|
40
|
+
@value = @value.map(&:dup)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get element at index +idx+, or element of name +name+
|
44
|
+
# @param [Integer, String, Symbol] idx_or_name
|
45
|
+
# @return [Object]
|
46
|
+
def [](idx_or_name)
|
47
|
+
case idx_or_name
|
48
|
+
when Integer
|
49
|
+
@value[idx]
|
50
|
+
when String, Symbol
|
51
|
+
@value.find { |elt| elt.name == idx_or_name }
|
52
|
+
end
|
39
53
|
end
|
40
54
|
|
41
55
|
private
|
@@ -43,14 +57,14 @@ module RASN1
|
|
43
57
|
def value_to_der
|
44
58
|
case @value
|
45
59
|
when Array
|
46
|
-
@value.map
|
60
|
+
@value.map(&:to_der).join
|
47
61
|
else
|
48
62
|
@value.to_s
|
49
63
|
end
|
50
64
|
end
|
51
65
|
|
52
|
-
def der_to_value(der, ber:false)
|
53
|
-
if @value.is_a?(Array)
|
66
|
+
def der_to_value(der, ber: false)
|
67
|
+
if @value.is_a?(Array) && !@value.empty?
|
54
68
|
nb_bytes = 0
|
55
69
|
@value.each do |element|
|
56
70
|
nb_bytes += element.parse!(der[nb_bytes..-1])
|
@@ -58,7 +72,7 @@ module RASN1
|
|
58
72
|
else
|
59
73
|
@value = der
|
60
74
|
der.length
|
61
|
-
end
|
75
|
+
end
|
62
76
|
end
|
63
77
|
end
|
64
78
|
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 OF
|
5
6
|
#
|
6
7
|
# A SEQUENCE OF is an array of one ASN.1 type.
|
@@ -45,7 +46,8 @@ module RASN1
|
|
45
46
|
# seqof[0][:int].value # => 12
|
46
47
|
# @author Sylvain Daubert
|
47
48
|
class SequenceOf < Constructed
|
48
|
-
|
49
|
+
# SequenceOf id value
|
50
|
+
ID = Sequence::ID
|
49
51
|
|
50
52
|
# @return [Class, Base]
|
51
53
|
attr_reader :of_type
|
@@ -68,7 +70,7 @@ module RASN1
|
|
68
70
|
def initialize_copy(other)
|
69
71
|
super
|
70
72
|
@of_type = @of_type.dup
|
71
|
-
@value = @value.map
|
73
|
+
@value = @value.map(&:dup)
|
72
74
|
end
|
73
75
|
|
74
76
|
# Add an item to SEQUENCE OF
|
@@ -76,9 +78,11 @@ module RASN1
|
|
76
78
|
def <<(obj)
|
77
79
|
if of_type_class < Primitive
|
78
80
|
raise ASN1Error, 'object to add should be an Array' unless obj.is_a?(Array)
|
81
|
+
|
79
82
|
@value += obj.map { |item| @of_type.new(item) }
|
80
83
|
elsif composed_of_type?
|
81
84
|
raise ASN1Error, 'object to add should be an Array' unless obj.is_a?(Array)
|
85
|
+
|
82
86
|
new_value = of_type_class.new
|
83
87
|
@of_type.value.each_with_index do |type, i|
|
84
88
|
type2 = type.dup
|
@@ -112,17 +116,15 @@ module RASN1
|
|
112
116
|
end
|
113
117
|
|
114
118
|
def inspect(level=0)
|
115
|
-
str =
|
116
|
-
str <<
|
117
|
-
|
118
|
-
level = level.abs
|
119
|
-
str << "#{type}:\n"
|
120
|
-
level += 1
|
119
|
+
str = common_inspect(level)
|
120
|
+
str << "\n"
|
121
|
+
level = level.abs + 1
|
121
122
|
@value.each do |item|
|
122
123
|
case item
|
123
124
|
when Base, Model
|
124
|
-
next if item.optional?
|
125
|
-
|
125
|
+
next if item.optional? && item.value.nil?
|
126
|
+
|
127
|
+
str << item.inspect(level)
|
126
128
|
str << "\n" unless str.end_with?("\n")
|
127
129
|
else
|
128
130
|
str << ' ' * level + "#{item.inspect}\n"
|
@@ -142,10 +144,10 @@ module RASN1
|
|
142
144
|
end
|
143
145
|
|
144
146
|
def value_to_der
|
145
|
-
@value.map
|
147
|
+
@value.map(&:to_der).join
|
146
148
|
end
|
147
149
|
|
148
|
-
def der_to_value(der, ber:false)
|
150
|
+
def der_to_value(der, ber: false)
|
149
151
|
@value = []
|
150
152
|
nb_bytes = 0
|
151
153
|
|
data/lib/rasn1/types/set.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 set
|
5
6
|
#
|
6
7
|
# A set is a collection of another ASN.1 types.
|
@@ -20,7 +21,8 @@ module RASN1
|
|
20
21
|
# ]
|
21
22
|
# @author Sylvain Daubert
|
22
23
|
class Set < Sequence
|
23
|
-
|
24
|
+
# Set id value
|
25
|
+
ID = 0x11
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
data/lib/rasn1/types/set_of.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RASN1
|
2
4
|
module Types
|
3
|
-
|
4
5
|
# ASN.1 SET OF
|
5
6
|
# @author Sylvain Daubert
|
6
7
|
class SetOf < SequenceOf
|
7
|
-
|
8
|
+
# SetOf id value
|
9
|
+
ID = Set::ID
|
8
10
|
|
9
11
|
# A SET OF is encoded as a SET.
|
10
12
|
# @return ['SET']
|
@@ -14,4 +16,3 @@ module RASN1
|
|
14
16
|
end
|
15
17
|
end
|
16
18
|
end
|
17
|
-
|
data/lib/rasn1/types/utc_time.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
|
3
5
|
module RASN1
|
4
6
|
module Types
|
5
|
-
|
6
7
|
# ASN.1 UTCTime
|
7
8
|
#
|
8
9
|
# +{#value} of a +UtcTime+ should be a ruby Time.
|
@@ -17,7 +18,8 @@ module RASN1
|
|
17
18
|
# not.
|
18
19
|
# @author Sylvain Daubert
|
19
20
|
class UtcTime < Primitive
|
20
|
-
|
21
|
+
# UtcTime id value
|
22
|
+
ID = 23
|
21
23
|
|
22
24
|
# Get ASN.1 type
|
23
25
|
# @return [String]
|
@@ -26,11 +28,11 @@ module RASN1
|
|
26
28
|
end
|
27
29
|
|
28
30
|
private
|
29
|
-
|
31
|
+
|
30
32
|
def value_to_der
|
31
33
|
@value.getutc.strftime('%y%m%d%H%M%SZ')
|
32
34
|
end
|
33
|
-
|
35
|
+
|
34
36
|
def der_to_value(der, ber: false)
|
35
37
|
format = case der.size
|
36
38
|
when 11
|