rasn1 0.12.0 → 0.13.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 +1 -0
- data/lib/rasn1/errors.rb +2 -1
- data/lib/rasn1/model.rb +25 -1
- data/lib/rasn1/tracer.rb +174 -0
- data/lib/rasn1/types/any.rb +34 -13
- data/lib/rasn1/types/base.rb +125 -22
- data/lib/rasn1/types/bit_string.rb +4 -2
- data/lib/rasn1/types/boolean.rb +6 -0
- data/lib/rasn1/types/choice.rb +15 -4
- data/lib/rasn1/types/constrained.rb +6 -1
- data/lib/rasn1/types/generalized_time.rb +38 -28
- data/lib/rasn1/types/integer.rb +17 -6
- data/lib/rasn1/types/null.rb +2 -0
- data/lib/rasn1/types/octet_string.rb +2 -0
- data/lib/rasn1/types/sequence.rb +7 -2
- data/lib/rasn1/types/sequence_of.rb +8 -2
- data/lib/rasn1/types/universal_string.rb +30 -0
- data/lib/rasn1/types/utc_time.rb +6 -0
- data/lib/rasn1/types.rb +9 -0
- data/lib/rasn1/version.rb +1 -1
- data/lib/rasn1/wrapper.rb +30 -22
- data/lib/rasn1.rb +17 -16
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 50b3f9ddc3a11279f36c21d14768a908b0fe4f22ff75d650791a7a58ac47eaa7
|
4
|
+
data.tar.gz: d229ea04a626e34d8411e3787101ace2c55e3cef7178b617073ed6b65fefb867
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37ea433265c6229a89d896e47918c242b2edc48c24ffa585873e3706dfc3eaf73628b76c91bad3e15e1d969a5caf52237c92336aefdba2668f63f9468e7e2004
|
7
|
+
data.tar.gz: 0035453de20ea4873999ec18eb3065afa9f1fdddc2927f446ecd2b1851a3a0d1bef32b5bbc2d9029f804a57cd71fefbf95dae4cd4babae5f8491f1e9ea8057ad
|
data/README.md
CHANGED
data/lib/rasn1/errors.rb
CHANGED
@@ -18,7 +18,7 @@ module RASN1
|
|
18
18
|
# Enumerated error
|
19
19
|
class EnumeratedError < Error; end
|
20
20
|
|
21
|
-
# CHOICE error: #chosen not set
|
21
|
+
# CHOICE error: {Types::Choice#chosen} not set
|
22
22
|
class ChoiceError < RASN1::Error
|
23
23
|
# @param [Types::Base] object
|
24
24
|
def initialize(object)
|
@@ -26,6 +26,7 @@ module RASN1
|
|
26
26
|
super()
|
27
27
|
end
|
28
28
|
|
29
|
+
# @return [String]
|
29
30
|
def message
|
30
31
|
"CHOICE #{@object.name}: #chosen not set"
|
31
32
|
end
|
data/lib/rasn1/model.rb
CHANGED
@@ -66,6 +66,9 @@ module RASN1
|
|
66
66
|
class Model # rubocop:disable Metrics/ClassLength
|
67
67
|
# @private
|
68
68
|
Elem = Struct.new(:name, :proc_or_class, :content) do
|
69
|
+
# @param [String,Symbol] name
|
70
|
+
# @param [Proc,Class] proc_or_class
|
71
|
+
# @param [Hash,nil] content
|
69
72
|
def initialize(name, proc_or_class, content)
|
70
73
|
if content.is_a?(Array)
|
71
74
|
duplicate_names = find_all_duplicate_names(content.map(&:name) + [name])
|
@@ -88,11 +91,13 @@ module RASN1
|
|
88
91
|
|
89
92
|
# @private
|
90
93
|
WrapElem = Struct.new(:element, :options) do
|
94
|
+
# @return [String]
|
91
95
|
def name
|
92
96
|
"#{element.name}_wrapper"
|
93
97
|
end
|
94
98
|
end
|
95
99
|
|
100
|
+
# Define helper methods to define models
|
96
101
|
module Accel
|
97
102
|
# @return [Hash]
|
98
103
|
attr_reader :options
|
@@ -145,6 +150,9 @@ module RASN1
|
|
145
150
|
klass.class_eval { @root = root }
|
146
151
|
end
|
147
152
|
|
153
|
+
# @private
|
154
|
+
# @param [String,Symbol] accel_name
|
155
|
+
# @param [Class] klass
|
148
156
|
# @since 0.11.0
|
149
157
|
# @since 0.12.0 track source location on error (adfoster-r7)
|
150
158
|
def define_type_accel_base(accel_name, klass)
|
@@ -159,6 +167,9 @@ module RASN1
|
|
159
167
|
EVAL
|
160
168
|
end
|
161
169
|
|
170
|
+
# @private
|
171
|
+
# @param [String,Symbol] accel_name
|
172
|
+
# @param [Class] klass
|
162
173
|
# @since 0.11.0
|
163
174
|
# @since 0.12.0 track source location on error (adfoster-r7)
|
164
175
|
def define_type_accel_of(accel_name, klass)
|
@@ -175,8 +186,9 @@ module RASN1
|
|
175
186
|
|
176
187
|
# Define an accelarator to access a type in a model definition
|
177
188
|
# @param [String] accel_name
|
178
|
-
# @param [Class] klass
|
189
|
+
# @param [Class] klass class to instanciate
|
179
190
|
# @since 0.11.0
|
191
|
+
# @since 0.12.0 track source location on error (adfoster-r7)
|
180
192
|
def define_type_accel(accel_name, klass)
|
181
193
|
if klass < Types::SequenceOf
|
182
194
|
define_type_accel_of(accel_name, klass)
|
@@ -287,6 +299,12 @@ module RASN1
|
|
287
299
|
# @param [Hash] options
|
288
300
|
# @return [Elem]
|
289
301
|
# @see Types::BitString#initialize
|
302
|
+
# @!method bmp_string(name, options)
|
303
|
+
# @!scope class
|
304
|
+
# @param [Symbol,String] name name of object in model
|
305
|
+
# @param [Hash] options
|
306
|
+
# @return [Elem]
|
307
|
+
# @see Types::BmpString#initialize
|
290
308
|
# @!method octet_string(name, options)
|
291
309
|
# @!scope class
|
292
310
|
# @param [Symbol,String] name name of object in model
|
@@ -305,6 +323,12 @@ module RASN1
|
|
305
323
|
# @param [Hash] options
|
306
324
|
# @return [Elem]
|
307
325
|
# @see Types::Enumerated#initialize
|
326
|
+
# @!method universal_string(name, options)
|
327
|
+
# @!scope class
|
328
|
+
# @param [Symbol,String] name name of object in model
|
329
|
+
# @param [Hash] options
|
330
|
+
# @return [Elem]
|
331
|
+
# @see Types::UniversalString#initialize
|
308
332
|
# @!method utf8_string(name, options)
|
309
333
|
# @!scope class
|
310
334
|
# @param [Symbol,String] name name of object in model
|
data/lib/rasn1/tracer.rb
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RASN1
|
4
|
+
# @private
|
5
|
+
class Tracer
|
6
|
+
# @return [IO]
|
7
|
+
attr_reader :io
|
8
|
+
# @return [Integer]
|
9
|
+
attr_accessor :tracing_level
|
10
|
+
|
11
|
+
TRACED_CLASSES = [Types::Any, Types::Choice, Types::Sequence, Types::SequenceOf, Types::Base].freeze
|
12
|
+
|
13
|
+
# @param [IO] io
|
14
|
+
def initialize(io)
|
15
|
+
@io = io
|
16
|
+
@tracing_level = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
# Puts +msg+ onto {#io}.
|
20
|
+
# @param [String] msg
|
21
|
+
# @return [void]
|
22
|
+
def trace(msg)
|
23
|
+
@io.puts(indent << msg)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return identation for given +level+. If +nil+, use {#tracing_level}.
|
27
|
+
# @param [Integer,nil] level
|
28
|
+
# @return [String]
|
29
|
+
def indent(level=nil)
|
30
|
+
level ||= @tracing_level
|
31
|
+
' ' * level
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Trace RASN1 parsing to +io+.
|
36
|
+
# All parsing methods called in block are traced to +io+. Each ASN.1 element is
|
37
|
+
# traced in a line showing element's id, its length and its data.
|
38
|
+
# @param [IO] io
|
39
|
+
# @example
|
40
|
+
# RASN1.trace do
|
41
|
+
# RASN1.parse("\x02\x01\x01") # puts "INTEGER id: 2 (0x02), len: 1 (0x01), data: 0x01"
|
42
|
+
# end
|
43
|
+
# RASN1.parse("\x01\x01\xff") # puts nothing onto STDOUT
|
44
|
+
# @return [void]
|
45
|
+
def self.trace(io=$stdout)
|
46
|
+
@tracer = Tracer.new(io)
|
47
|
+
Tracer::TRACED_CLASSES.each(&:start_tracing)
|
48
|
+
|
49
|
+
begin
|
50
|
+
yield @tracer
|
51
|
+
ensure
|
52
|
+
Tracer::TRACED_CLASSES.reverse.each(&:stop_tracing)
|
53
|
+
@tracer.io.flush
|
54
|
+
@tracer = nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# @private
|
59
|
+
def self.tracer
|
60
|
+
@tracer
|
61
|
+
end
|
62
|
+
|
63
|
+
module Types
|
64
|
+
class Base
|
65
|
+
class << self
|
66
|
+
# @private
|
67
|
+
# Patch {#do_parse} to add tracing ability
|
68
|
+
def start_tracing
|
69
|
+
alias_method :do_parse_without_tracing, :do_parse
|
70
|
+
alias_method :do_parse, :do_parse_with_tracing
|
71
|
+
alias_method :do_parse_explicit_without_tracing, :do_parse_explicit
|
72
|
+
alias_method :do_parse_explicit, :do_parse_explicit_with_tracing
|
73
|
+
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
# Unpatch {#do_parse} to remove tracing ability
|
77
|
+
def stop_tracing
|
78
|
+
alias_method :do_parse, :do_parse_without_tracing # rubocop:disable Lint/DuplicateMethods
|
79
|
+
alias_method :do_parse_explicit, :do_parse_explicit_without_tracing # rubocop:disable Lint/DuplicateMethods
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# @private
|
84
|
+
# Parse +der+ with tracing abillity
|
85
|
+
# @see #parse!
|
86
|
+
def do_parse_with_tracing(der, ber)
|
87
|
+
ret = do_parse_without_tracing(der, ber)
|
88
|
+
RASN1.tracer.trace(self.trace)
|
89
|
+
ret
|
90
|
+
end
|
91
|
+
|
92
|
+
def do_parse_explicit_with_tracing(data)
|
93
|
+
RASN1.tracer.tracing_level += 1
|
94
|
+
do_parse_explicit_without_tracing(data)
|
95
|
+
RASN1.tracer.tracing_level -= 1
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Choice
|
100
|
+
class << self
|
101
|
+
# @private
|
102
|
+
# Patch {#parse!} to add tracing ability
|
103
|
+
def start_tracing
|
104
|
+
alias_method :parse_without_tracing, :parse!
|
105
|
+
alias_method :parse!, :parse_with_tracing
|
106
|
+
end
|
107
|
+
|
108
|
+
# @private
|
109
|
+
# Unpatch {#parse!} to remove tracing ability
|
110
|
+
def stop_tracing
|
111
|
+
alias_method :parse!, :parse_without_tracing # rubocop:disable Lint/DuplicateMethods
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# @private
|
116
|
+
# Parse +der+ with tracing abillity
|
117
|
+
# @see #parse!
|
118
|
+
def parse_with_tracing(der, ber: false)
|
119
|
+
RASN1.tracer.trace(self.trace)
|
120
|
+
parse_without_tracing(der, ber: ber)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Sequence
|
125
|
+
class << self
|
126
|
+
# @private
|
127
|
+
# Patch {#der_to_value} to add tracing ability
|
128
|
+
def start_tracing
|
129
|
+
alias_method :der_to_value_without_tracing, :der_to_value
|
130
|
+
alias_method :der_to_value, :der_to_value_with_tracing
|
131
|
+
end
|
132
|
+
|
133
|
+
# @private
|
134
|
+
# Unpatch {#der_to_value!} to remove tracing ability
|
135
|
+
def stop_tracing
|
136
|
+
alias_method :der_to_value, :der_to_value_without_tracing # rubocop:disable Lint/DuplicateMethods
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# @private
|
141
|
+
# der_to_value +der+ with tracing abillity
|
142
|
+
def der_to_value_with_tracing(der, ber: false)
|
143
|
+
RASN1.tracer.tracing_level += 1
|
144
|
+
der_to_value_without_tracing(der, ber: ber)
|
145
|
+
RASN1.tracer.tracing_level -= 1
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
class SequenceOf
|
150
|
+
class << self
|
151
|
+
# @private
|
152
|
+
# Patch {#der_to_value} to add tracing ability
|
153
|
+
def start_tracing
|
154
|
+
alias_method :der_to_value_without_tracing, :der_to_value
|
155
|
+
alias_method :der_to_value, :der_to_value_with_tracing
|
156
|
+
end
|
157
|
+
|
158
|
+
# @private
|
159
|
+
# Unpatch {#der_to_value!} to remove tracing ability
|
160
|
+
def stop_tracing
|
161
|
+
alias_method :der_to_value, :der_to_value_without_tracing # rubocop:disable Lint/DuplicateMethods
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# @private
|
166
|
+
# der_to_value +der+ with tracing abillity
|
167
|
+
def der_to_value_with_tracing(der, ber: false)
|
168
|
+
RASN1.tracer.tracing_level += 1
|
169
|
+
der_to_value_without_tracing(der, ber: ber)
|
170
|
+
RASN1.tracer.tracing_level -= 1
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/rasn1/types/any.rb
CHANGED
@@ -31,22 +31,12 @@ module RASN1
|
|
31
31
|
# @param [Boolean] ber if +true+, accept BER encoding
|
32
32
|
# @return [Integer] total number of parsed bytes
|
33
33
|
def parse!(der, ber: false)
|
34
|
-
|
35
|
-
return 0 if optional?
|
36
|
-
|
37
|
-
raise ASN1Error, 'Expected ANY but get nothing'
|
38
|
-
end
|
39
|
-
|
40
|
-
id_size = Types.decode_identifier_octets(der).last
|
41
|
-
total_length, = get_data(der[id_size..-1], ber)
|
42
|
-
total_length += id_size
|
43
|
-
|
44
|
-
@no_value = false
|
45
|
-
@value = der[0, total_length]
|
46
|
-
|
34
|
+
total_length, _data = do_parse(der, ber)
|
47
35
|
total_length
|
48
36
|
end
|
49
37
|
|
38
|
+
# @param [::Integer] level
|
39
|
+
# @return [String]
|
50
40
|
def inspect(level=0)
|
51
41
|
str = common_inspect(level)
|
52
42
|
str << if !value?
|
@@ -60,6 +50,16 @@ module RASN1
|
|
60
50
|
end
|
61
51
|
end
|
62
52
|
|
53
|
+
# @private Tracer private API
|
54
|
+
# @return [String]
|
55
|
+
def trace
|
56
|
+
return trace_any if value?
|
57
|
+
|
58
|
+
msg_type(no_id: true) << ' NONE'
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
63
|
def common_inspect(level)
|
64
64
|
lvl = level >= 0 ? level : 0
|
65
65
|
str = ' ' * lvl
|
@@ -69,6 +69,27 @@ module RASN1
|
|
69
69
|
str << "[#{id}] IMPLICIT " if implicit?
|
70
70
|
str << '(ANY) '
|
71
71
|
end
|
72
|
+
|
73
|
+
def do_parse(der, ber)
|
74
|
+
if der.empty?
|
75
|
+
return [0, ''] if optional?
|
76
|
+
|
77
|
+
raise ASN1Error, 'Expected ANY but get nothing'
|
78
|
+
end
|
79
|
+
|
80
|
+
id_size = Types.decode_identifier_octets(der).last
|
81
|
+
total_length, = get_data(der[id_size..-1], ber)
|
82
|
+
total_length += id_size
|
83
|
+
|
84
|
+
@no_value = false
|
85
|
+
@value = der[0, total_length]
|
86
|
+
|
87
|
+
[total_length, @value]
|
88
|
+
end
|
89
|
+
|
90
|
+
def trace_any
|
91
|
+
msg_type(no_id: true) << trace_data(value)
|
92
|
+
end
|
72
93
|
end
|
73
94
|
end
|
74
95
|
end
|
data/lib/rasn1/types/base.rb
CHANGED
@@ -71,6 +71,12 @@ module RASN1
|
|
71
71
|
attr_reader :default
|
72
72
|
# @return [Hash[Symbol, Object]]
|
73
73
|
attr_reader :options
|
74
|
+
# @return [String] raw parsed data
|
75
|
+
attr_reader :raw_data
|
76
|
+
# @return [String] raw parsed length
|
77
|
+
attr_reader :raw_length
|
78
|
+
|
79
|
+
private :raw_data, :raw_length
|
74
80
|
|
75
81
|
# Get ASN.1 type
|
76
82
|
# @return [String]
|
@@ -121,13 +127,16 @@ module RASN1
|
|
121
127
|
set_value(options.delete(:value))
|
122
128
|
self.options = options
|
123
129
|
specific_initializer
|
130
|
+
@raw_data = ''.b
|
131
|
+
@raw_length = ''.b
|
124
132
|
end
|
125
133
|
|
126
134
|
# @abstract To help subclass initialize itself. Default implementation do nothing.
|
127
135
|
def specific_initializer; end
|
128
136
|
|
129
|
-
#
|
130
|
-
def initialize_copy(
|
137
|
+
# Deep copy @value and @default.
|
138
|
+
def initialize_copy(*)
|
139
|
+
super
|
131
140
|
@value = @value.dup
|
132
141
|
@no_value = @no_value.dup
|
133
142
|
@default = @default.dup
|
@@ -153,6 +162,7 @@ module RASN1
|
|
153
162
|
''
|
154
163
|
end
|
155
164
|
|
165
|
+
# Say if this type is optional
|
156
166
|
# @return [::Boolean]
|
157
167
|
def optional?
|
158
168
|
@optional
|
@@ -215,17 +225,11 @@ module RASN1
|
|
215
225
|
# @return [Integer] total number of parsed bytes
|
216
226
|
# @raise [ASN1Error] error on parsing
|
217
227
|
def parse!(der, ber: false)
|
218
|
-
|
228
|
+
total_length, data = do_parse(der, ber)
|
229
|
+
return 0 if total_length.zero?
|
219
230
|
|
220
|
-
id_size = Types.decode_identifier_octets(der).last
|
221
|
-
total_length, data = get_data(der[id_size..-1], ber)
|
222
|
-
total_length += id_size
|
223
|
-
@no_value = false
|
224
231
|
if explicit?
|
225
|
-
|
226
|
-
type = explicit_type
|
227
|
-
type.parse!(data)
|
228
|
-
@value = type.value
|
232
|
+
do_parse_explicit(data)
|
229
233
|
else
|
230
234
|
der_to_value(data, ber: ber)
|
231
235
|
end
|
@@ -283,8 +287,82 @@ module RASN1
|
|
283
287
|
value? && (@default.nil? || (@value != @default))
|
284
288
|
end
|
285
289
|
|
290
|
+
# @private Tracer private API
|
291
|
+
# @return [String]
|
292
|
+
def trace
|
293
|
+
return trace_real if value?
|
294
|
+
|
295
|
+
msg = msg_type
|
296
|
+
if default.nil? # rubocop:disable Style/ConditionalAssignment
|
297
|
+
msg << ' NONE'
|
298
|
+
else
|
299
|
+
msg << " DEFAULT VALUE #{default}"
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
286
303
|
private
|
287
304
|
|
305
|
+
def trace_real
|
306
|
+
encoded_id = unpack(encode_identifier_octets)
|
307
|
+
data_length = raw_data.length
|
308
|
+
encoded_length = unpack(raw_length)
|
309
|
+
msg = msg_type
|
310
|
+
msg << " (0x#{encoded_id}),"
|
311
|
+
msg << " len: #{data_length} (0x#{encoded_length})"
|
312
|
+
msg << trace_data
|
313
|
+
end
|
314
|
+
|
315
|
+
def trace_data(data=nil)
|
316
|
+
byte_count = 0
|
317
|
+
data ||= raw_data
|
318
|
+
|
319
|
+
lines = []
|
320
|
+
str = ''
|
321
|
+
data.each_byte do |byte|
|
322
|
+
if (byte_count % 16).zero?
|
323
|
+
str = trace_format_new_data_line(byte_count)
|
324
|
+
lines << str
|
325
|
+
end
|
326
|
+
str[compute_trace_index(byte_count, 3), 2] = '%02x' % byte
|
327
|
+
str[compute_trace_index(byte_count, 1, 49)] = byte >= 32 && byte <= 126 ? byte.chr : '.'
|
328
|
+
byte_count += 1
|
329
|
+
end
|
330
|
+
lines.map(&:rstrip).join << "\n"
|
331
|
+
end
|
332
|
+
|
333
|
+
def trace_format_new_data_line(count)
|
334
|
+
head_line = RASN1.tracer.indent(RASN1.tracer.tracing_level + 1)
|
335
|
+
("\n#{head_line}%04x " % count) << ' ' * 68
|
336
|
+
end
|
337
|
+
|
338
|
+
def compute_trace_index(byte_count, byte_count_mul=1, offset=0)
|
339
|
+
base_idx = 7 + RASN1.tracer.indent(RASN1.tracer.tracing_level + 1).length
|
340
|
+
base_idx + offset + (byte_count % 16) * byte_count_mul
|
341
|
+
end
|
342
|
+
|
343
|
+
def unpack(binstr)
|
344
|
+
binstr.unpack1('H*')
|
345
|
+
end
|
346
|
+
|
347
|
+
def asn1_class_to_s
|
348
|
+
asn1_class == :universal ? '' : asn1_class.to_s.upcase << ' '
|
349
|
+
end
|
350
|
+
|
351
|
+
def msg_type(no_id: false)
|
352
|
+
msg = name.nil? ? +'' : +"#{name} "
|
353
|
+
msg << "[ #{asn1_class_to_s}#{id} ] " unless no_id
|
354
|
+
msg << if explicit?
|
355
|
+
+'EXPLICIT '
|
356
|
+
elsif implicit?
|
357
|
+
+'IMPLICIT '
|
358
|
+
else
|
359
|
+
+''
|
360
|
+
end
|
361
|
+
msg << type
|
362
|
+
msg << ' OPTIONAL' if optional?
|
363
|
+
msg
|
364
|
+
end
|
365
|
+
|
288
366
|
def pc_bit
|
289
367
|
if @constructed.nil?
|
290
368
|
self.class.const_get(:ASN1_PC)
|
@@ -299,7 +377,7 @@ module RASN1
|
|
299
377
|
lvl = level >= 0 ? level : 0
|
300
378
|
str = ' ' * lvl
|
301
379
|
str << "#{@name} " unless @name.nil?
|
302
|
-
str <<
|
380
|
+
str << asn1_class_to_s
|
303
381
|
str << "[#{id}] EXPLICIT " if explicit?
|
304
382
|
str << "[#{id}] IMPLICIT " if implicit?
|
305
383
|
str << "#{type}:"
|
@@ -313,6 +391,24 @@ module RASN1
|
|
313
391
|
end
|
314
392
|
end
|
315
393
|
|
394
|
+
def do_parse(der, ber)
|
395
|
+
return [0, ''] unless check_id(der)
|
396
|
+
|
397
|
+
id_size = Types.decode_identifier_octets(der).last
|
398
|
+
total_length, data = get_data(der[id_size..-1], ber)
|
399
|
+
total_length += id_size
|
400
|
+
@no_value = false
|
401
|
+
|
402
|
+
[total_length, data]
|
403
|
+
end
|
404
|
+
|
405
|
+
def do_parse_explicit(data)
|
406
|
+
# Delegate to #explicit type to generate sub-value
|
407
|
+
type = explicit_type
|
408
|
+
type.parse!(data)
|
409
|
+
@value = type.value
|
410
|
+
end
|
411
|
+
|
316
412
|
def value_to_der
|
317
413
|
case @value
|
318
414
|
when Base
|
@@ -454,6 +550,21 @@ module RASN1
|
|
454
550
|
end
|
455
551
|
|
456
552
|
def get_data(der, ber)
|
553
|
+
return [0, ''] if der.nil? || der.empty?
|
554
|
+
|
555
|
+
length, length_length = get_length(der, ber)
|
556
|
+
|
557
|
+
data = der[1 + length_length, length]
|
558
|
+
@raw_length = der[0, length_length + 1]
|
559
|
+
@raw_data = data
|
560
|
+
|
561
|
+
total_length = 1 + length
|
562
|
+
total_length += length_length if length_length.positive?
|
563
|
+
|
564
|
+
[total_length, data]
|
565
|
+
end
|
566
|
+
|
567
|
+
def get_length(der, ber)
|
457
568
|
length = der.unpack1('C').to_i
|
458
569
|
length_length = 0
|
459
570
|
|
@@ -464,12 +575,8 @@ module RASN1
|
|
464
575
|
length = der[1, length_length].unpack('C*')
|
465
576
|
.reduce(0) { |len, b| (len << 8) | b }
|
466
577
|
end
|
467
|
-
data = der[1 + length_length, length]
|
468
578
|
|
469
|
-
|
470
|
-
total_length += length_length if length_length.positive?
|
471
|
-
|
472
|
-
[total_length, data]
|
579
|
+
[length, length_length]
|
473
580
|
end
|
474
581
|
|
475
582
|
def raise_on_indefinite_length(ber)
|
@@ -484,7 +591,7 @@ module RASN1
|
|
484
591
|
end
|
485
592
|
|
486
593
|
def explicit_type
|
487
|
-
self.class.new
|
594
|
+
self.class.new(name: name)
|
488
595
|
end
|
489
596
|
|
490
597
|
def raise_id_error(der)
|
@@ -493,10 +600,6 @@ module RASN1
|
|
493
600
|
raise ASN1Error, msg
|
494
601
|
end
|
495
602
|
|
496
|
-
def class_from_numeric_id(id)
|
497
|
-
CLASSES.key(id & CLASS_MASK)
|
498
|
-
end
|
499
|
-
|
500
603
|
def self2name
|
501
604
|
name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
|
502
605
|
if implicit? || explicit?
|
@@ -8,7 +8,6 @@ module RASN1
|
|
8
8
|
# BitString id value
|
9
9
|
ID = 3
|
10
10
|
|
11
|
-
# @param [Integer] bit_length
|
12
11
|
# @return [Integer]
|
13
12
|
attr_writer :bit_length
|
14
13
|
|
@@ -42,6 +41,9 @@ module RASN1
|
|
42
41
|
str << " #{value.inspect} (bit length: #{bit_length})"
|
43
42
|
end
|
44
43
|
|
44
|
+
# Same as {Base#can_build?} but also check bit_length
|
45
|
+
# @see Base#can_build?
|
46
|
+
# @return [Boolean]
|
45
47
|
def can_build?
|
46
48
|
super || (!@default.nil? && (@bit_length != @default_bit_length))
|
47
49
|
end
|
@@ -86,7 +88,7 @@ module RASN1
|
|
86
88
|
end
|
87
89
|
|
88
90
|
def explicit_type
|
89
|
-
self.class.new(value: @value, bit_length: @bit_length)
|
91
|
+
self.class.new(name: name, value: @value, bit_length: @bit_length)
|
90
92
|
end
|
91
93
|
end
|
92
94
|
end
|
data/lib/rasn1/types/boolean.rb
CHANGED
data/lib/rasn1/types/choice.rb
CHANGED
@@ -75,28 +75,39 @@ module RASN1
|
|
75
75
|
# @return [Integer] total number of parsed bytes
|
76
76
|
# @raise [ASN1Error] error on parsing
|
77
77
|
def parse!(der, ber: false)
|
78
|
-
parsed = false
|
79
78
|
@value.each_with_index do |element, i|
|
80
79
|
@chosen = i
|
81
80
|
nb_bytes = element.parse!(der, ber: ber)
|
82
|
-
parsed = true
|
83
81
|
return nb_bytes
|
84
82
|
rescue ASN1Error
|
85
83
|
@chosen = nil
|
86
84
|
next
|
87
85
|
end
|
88
|
-
|
86
|
+
|
87
|
+
@no_value = true
|
88
|
+
@value = void_value
|
89
|
+
raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless optional?
|
90
|
+
|
91
|
+
0
|
89
92
|
end
|
90
93
|
|
94
|
+
# @param [::Integer] level
|
95
|
+
# @return [String]
|
91
96
|
def inspect(level=0)
|
92
97
|
str = common_inspect(level)
|
93
|
-
str << if defined?
|
98
|
+
str << if defined?(@chosen) && value?
|
94
99
|
"\n#{@value[@chosen].inspect(level + 1)}"
|
95
100
|
else
|
96
101
|
' not chosen!'
|
97
102
|
end
|
98
103
|
end
|
99
104
|
|
105
|
+
# @private Tracer private API
|
106
|
+
# @return [String]
|
107
|
+
def trace
|
108
|
+
msg_type(no_id: true)
|
109
|
+
end
|
110
|
+
|
100
111
|
private
|
101
112
|
|
102
113
|
def check_chosen
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module RASN1
|
4
4
|
module Types
|
5
|
-
# Mixin to
|
5
|
+
# Mixin to add constraints on a RASN1 type.
|
6
6
|
# Should not be used directly but through {Types.define_type}.
|
7
7
|
# @version 0.11.0
|
8
8
|
# @author Sylvain Daubert
|
9
9
|
module Constrained
|
10
|
+
# Define class/module methods for {Constrained} module
|
10
11
|
module ClassMethods
|
11
12
|
# Setter for constraint
|
12
13
|
# @param [Proc,nil] constraint
|
@@ -31,8 +32,10 @@ module RASN1
|
|
31
32
|
end
|
32
33
|
|
33
34
|
class << self
|
35
|
+
# @return [Proc] proc to check constraints
|
34
36
|
attr_reader :constraint
|
35
37
|
|
38
|
+
# Extend +base+ with {ClassMethods}
|
36
39
|
def included(base)
|
37
40
|
base.extend ClassMethods
|
38
41
|
end
|
@@ -46,6 +49,8 @@ module RASN1
|
|
46
49
|
super
|
47
50
|
end
|
48
51
|
|
52
|
+
private
|
53
|
+
|
49
54
|
def der_to_value(der, ber: false)
|
50
55
|
super
|
51
56
|
self.class.check_constraint(@value)
|
@@ -71,19 +71,33 @@ module RASN1
|
|
71
71
|
end
|
72
72
|
|
73
73
|
def value_when_fraction_empty(date_hour)
|
74
|
-
if
|
75
|
-
|
76
|
-
|
77
|
-
|
74
|
+
tz = if date_hour[-1] == 'Z'
|
75
|
+
date_hour.slice!(-1, 1)
|
76
|
+
'+00:00' # Ruby 3.0: to remove after end-of support of ruby 3.0
|
77
|
+
elsif date_hour.match?(/[+-]\d+$/)
|
78
|
+
# Ruby 3.0
|
79
|
+
# date_hour.slice!(-5, 5)
|
80
|
+
zone = date_hour.slice!(-5, 5)
|
81
|
+
zone[0, 3] << ':' << zone[3, 2]
|
82
|
+
end
|
83
|
+
year = date_hour.slice!(0, 4).to_i
|
84
|
+
others = date_hour.scan(/../).map(&:to_i)
|
85
|
+
# Ruby 3.0
|
86
|
+
# From 3.1: "Z" and "-0100" are supported
|
87
|
+
# Below 3.1: should be "-01:00" or "+00:00"
|
88
|
+
unless tz.nil?
|
89
|
+
others += [0] * (5 - others.size)
|
90
|
+
others << tz
|
78
91
|
end
|
79
|
-
|
80
|
-
|
92
|
+
@value = Time.new(year, *others)
|
93
|
+
# From 3.1: replace all this code by: Time.new(year, *others, in: tz)
|
81
94
|
end
|
82
95
|
|
83
96
|
def value_when_fraction_ends_with_z(date_hour, fraction)
|
84
97
|
fraction = fraction[0...-1]
|
85
98
|
date_hour << 'Z'
|
86
|
-
frac_base =
|
99
|
+
frac_base = compute_frac_base(date_hour)
|
100
|
+
value_when_fraction_empty(date_hour)
|
87
101
|
fix_value(fraction, frac_base)
|
88
102
|
end
|
89
103
|
|
@@ -93,47 +107,43 @@ module RASN1
|
|
93
107
|
# fraction contains fraction and timezone info. Split them
|
94
108
|
fraction = match[1]
|
95
109
|
date_hour << match[2]
|
96
|
-
else
|
97
|
-
# fraction only contains fraction.
|
98
|
-
# Have to add offset with UTC to force Strptime to
|
99
|
-
# generate a local time.
|
100
|
-
date_hour << Time.now.strftime('%z')
|
101
110
|
end
|
102
111
|
|
103
|
-
frac_base =
|
112
|
+
frac_base = compute_frac_base(date_hour)
|
113
|
+
value_when_fraction_empty(date_hour)
|
104
114
|
fix_value(fraction, frac_base)
|
105
115
|
end
|
106
116
|
|
107
|
-
def value_from(date_hour)
|
108
|
-
format, frac_base = strformat(date_hour)
|
109
|
-
@value = Strptime.new(format).exec(date_hour)
|
110
|
-
frac_base
|
111
|
-
end
|
112
|
-
|
113
117
|
def fix_value(fraction, frac_base)
|
114
118
|
frac = ".#{fraction}".to_r * frac_base
|
115
119
|
@value = (@value + frac) unless fraction.nil?
|
116
120
|
end
|
117
121
|
|
118
|
-
def
|
122
|
+
def compute_frac_base(date_hour)
|
119
123
|
case date_hour.size
|
120
|
-
when 11
|
121
|
-
|
122
|
-
when 13, 17
|
123
|
-
|
124
|
+
when 10, 11
|
125
|
+
HOUR_TO_SEC
|
126
|
+
when 12, 13, 17
|
127
|
+
MINUTE_TO_SEC
|
124
128
|
when 15
|
125
129
|
if date_hour[-1] == 'Z'
|
126
|
-
|
130
|
+
SECOND_TO_SEC
|
127
131
|
else
|
128
|
-
|
132
|
+
HOUR_TO_SEC
|
129
133
|
end
|
130
|
-
when 19
|
131
|
-
|
134
|
+
when 14, 19
|
135
|
+
SECOND_TO_SEC
|
132
136
|
else
|
133
137
|
prefix = @name.nil? ? type : "tag #{@name}"
|
134
138
|
raise ASN1Error, "#{prefix}: unrecognized format: #{date_hour}"
|
135
139
|
end
|
136
140
|
end
|
141
|
+
|
142
|
+
def trace_data
|
143
|
+
return super if explicit?
|
144
|
+
|
145
|
+
+' ' << raw_data
|
146
|
+
end
|
137
147
|
end
|
138
148
|
end
|
139
149
|
end
|
data/lib/rasn1/types/integer.rb
CHANGED
@@ -20,7 +20,7 @@ module RASN1
|
|
20
20
|
initialize_enum(@options[:enum])
|
21
21
|
end
|
22
22
|
|
23
|
-
# @param [Integer,String,Symbol,nil]
|
23
|
+
# @param [Integer,String,Symbol,nil] val
|
24
24
|
# @return [void]
|
25
25
|
def value=(val)
|
26
26
|
@no_value = false
|
@@ -128,18 +128,29 @@ module RASN1
|
|
128
128
|
v
|
129
129
|
end
|
130
130
|
|
131
|
+
def int_to_enum(int, check_enum: true)
|
132
|
+
raise EnumeratedError, "#{@name}: value #{int} not in enumeration" if check_enum && !@enum.value?(int)
|
133
|
+
|
134
|
+
@enum.key(int) || int
|
135
|
+
end
|
136
|
+
|
131
137
|
def der_to_value(der, ber: false)
|
132
138
|
@value = der_to_int_value(der, ber: ber)
|
133
139
|
return if @enum.empty?
|
134
140
|
|
135
|
-
|
136
|
-
raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless @enum.value?(@value)
|
137
|
-
|
138
|
-
@value = @enum.key(@value)
|
141
|
+
@value = int_to_enum(@value)
|
139
142
|
end
|
140
143
|
|
141
144
|
def explicit_type
|
142
|
-
self.class.new(name:
|
145
|
+
self.class.new(name: name, enum: enum)
|
146
|
+
end
|
147
|
+
|
148
|
+
def trace_data
|
149
|
+
return super if explicit?
|
150
|
+
return " #{der_to_int_value(raw_data)} (0x#{raw_data.unpack1('H*')})" if @enum.empty?
|
151
|
+
|
152
|
+
v = int_to_enum(der_to_int_value(raw_data), check_enum: false)
|
153
|
+
" #{v} (0x#{raw_data.unpack1('H*')})"
|
143
154
|
end
|
144
155
|
end
|
145
156
|
end
|
data/lib/rasn1/types/null.rb
CHANGED
data/lib/rasn1/types/sequence.rb
CHANGED
@@ -36,7 +36,8 @@ module RASN1
|
|
36
36
|
@value ||= []
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
# Deep copy @value
|
40
|
+
def initialize_copy(*)
|
40
41
|
super
|
41
42
|
@value = case @value
|
42
43
|
when Array
|
@@ -88,8 +89,12 @@ module RASN1
|
|
88
89
|
end
|
89
90
|
end
|
90
91
|
|
92
|
+
def trace_data
|
93
|
+
''
|
94
|
+
end
|
95
|
+
|
91
96
|
def explicit_type
|
92
|
-
self.class.new(value: @value)
|
97
|
+
self.class.new(name: name, value: @value)
|
93
98
|
end
|
94
99
|
end
|
95
100
|
end
|
@@ -67,7 +67,8 @@ module RASN1
|
|
67
67
|
@no_value = false
|
68
68
|
end
|
69
69
|
|
70
|
-
|
70
|
+
# Clone @#of_type and values
|
71
|
+
def initialize_copy(*)
|
71
72
|
super
|
72
73
|
@of_type = @of_type.dup
|
73
74
|
@value = @value.map(&:dup)
|
@@ -104,6 +105,7 @@ module RASN1
|
|
104
105
|
@value.length
|
105
106
|
end
|
106
107
|
|
108
|
+
# @param [::Integer] level
|
107
109
|
# @return [String]
|
108
110
|
def inspect(level=0)
|
109
111
|
str = common_inspect(level)
|
@@ -153,7 +155,7 @@ module RASN1
|
|
153
155
|
end
|
154
156
|
|
155
157
|
def explicit_type
|
156
|
-
self.class.new(self.of_type)
|
158
|
+
self.class.new(self.of_type, name: name)
|
157
159
|
end
|
158
160
|
|
159
161
|
def push_primitive(obj)
|
@@ -188,6 +190,10 @@ module RASN1
|
|
188
190
|
raise ASN1Error, "object to add should be a #{of_type_class} or a Hash"
|
189
191
|
end
|
190
192
|
end
|
193
|
+
|
194
|
+
def trace_data
|
195
|
+
''
|
196
|
+
end
|
191
197
|
end
|
192
198
|
end
|
193
199
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RASN1
|
4
|
+
module Types
|
5
|
+
# ASN.1 UniversalString
|
6
|
+
# @since 0.13.0
|
7
|
+
# @author zeroSteiner
|
8
|
+
class UniversalString < OctetString
|
9
|
+
# UniversalString id value
|
10
|
+
ID = 28
|
11
|
+
|
12
|
+
# Get ASN.1 type
|
13
|
+
# @return [String]
|
14
|
+
def self.type
|
15
|
+
'UniversalString'
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def value_to_der
|
21
|
+
@value.to_s.dup.encode('UTF-32BE').b
|
22
|
+
end
|
23
|
+
|
24
|
+
def der_to_value(der, ber: false)
|
25
|
+
super
|
26
|
+
@value = der.to_s.dup.force_encoding('UTF-32BE')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/rasn1/types/utc_time.rb
CHANGED
data/lib/rasn1/types.rb
CHANGED
@@ -87,8 +87,16 @@ module RASN1
|
|
87
87
|
# @param [Types::Base] from class from which inherits
|
88
88
|
# @param [Module] in_module module in which creates new type (default to {RASN1::Types})
|
89
89
|
# @return [Class] newly created class
|
90
|
+
# @yieldparam [Object] value value to set to type, or infered at parsing
|
91
|
+
# @yieldreturn [Boolean]
|
90
92
|
# @since 0.11.0
|
91
93
|
# @since 0.12.0 in_module parameter
|
94
|
+
# @example
|
95
|
+
# # Define a new UInt32 type
|
96
|
+
# # UInt32 ::= INTEGER (0 .. 4294967295)
|
97
|
+
# RASN1::Types.define_type('UInt32', from: RASN1::Types::Integer) do |value|
|
98
|
+
# (value >= 0) && (value < 2**32)
|
99
|
+
# end
|
92
100
|
def self.define_type(name, from:, in_module: self, &block)
|
93
101
|
constraint = block.nil? ? nil : block.to_proc
|
94
102
|
|
@@ -120,6 +128,7 @@ require_relative 'types/null'
|
|
120
128
|
require_relative 'types/object_id'
|
121
129
|
require_relative 'types/enumerated'
|
122
130
|
require_relative 'types/bmp_string'
|
131
|
+
require_relative 'types/universal_string'
|
123
132
|
require_relative 'types/utf8_string'
|
124
133
|
require_relative 'types/numeric_string'
|
125
134
|
require_relative 'types/printable_string'
|
data/lib/rasn1/version.rb
CHANGED
data/lib/rasn1/wrapper.rb
CHANGED
@@ -14,8 +14,8 @@ module RASN1
|
|
14
14
|
# @example
|
15
15
|
# # object to wrap
|
16
16
|
# int = RASN1::Types::Integer.new(implicit: 1) # its tag is 0x81
|
17
|
-
# # simple
|
18
|
-
# wrapper = RASN1::Wrapper.new(int,
|
17
|
+
# # simple wrapper, change an option
|
18
|
+
# wrapper = RASN1::Wrapper.new(int, default: 1)
|
19
19
|
# # implicit wrapper
|
20
20
|
# wrapper = RASN1::Wrapper.new(int, implicit: 3) # wrapped int tag is now 0x83
|
21
21
|
# # explicit wrapper
|
@@ -70,26 +70,6 @@ module RASN1
|
|
70
70
|
super(element)
|
71
71
|
end
|
72
72
|
|
73
|
-
def explicit_implicit(options)
|
74
|
-
opts = options.dup
|
75
|
-
@explicit = opts.delete(:explicit)
|
76
|
-
@implicit = opts.delete(:implicit)
|
77
|
-
opts
|
78
|
-
end
|
79
|
-
|
80
|
-
def generate_explicit_wrapper(options)
|
81
|
-
# ExplicitWrapper is a hand-made explicit tag, but we have to use its implicit option
|
82
|
-
# to force its tag value.
|
83
|
-
@explicit_wrapper = ExplicitWrapper.new(options.merge(implicit: @explicit))
|
84
|
-
end
|
85
|
-
|
86
|
-
def generate_explicit_wrapper_options(options)
|
87
|
-
new_opts = {}
|
88
|
-
new_opts[:default] = options[:default] if options.key?(:default)
|
89
|
-
new_opts[:optional] = options[:optional] if options.key?(:optional)
|
90
|
-
new_opts
|
91
|
-
end
|
92
|
-
|
93
73
|
# Say if wrapper is an explicit one (i.e. add tag and length to its element)
|
94
74
|
# @return [Boolean]
|
95
75
|
def explicit?
|
@@ -136,6 +116,8 @@ module RASN1
|
|
136
116
|
end
|
137
117
|
end
|
138
118
|
|
119
|
+
# @return [Boolean]
|
120
|
+
# @see Types::Base#value?
|
139
121
|
def value?
|
140
122
|
if explicit?
|
141
123
|
@explicit_wrapper.value?
|
@@ -151,6 +133,7 @@ module RASN1
|
|
151
133
|
end
|
152
134
|
|
153
135
|
# @return [::Integer]
|
136
|
+
# @see Types::Base#id
|
154
137
|
def id
|
155
138
|
if implicit?
|
156
139
|
@implicit
|
@@ -162,6 +145,7 @@ module RASN1
|
|
162
145
|
end
|
163
146
|
|
164
147
|
# @return [Symbol]
|
148
|
+
# @see Types::Base#asn1_class
|
165
149
|
def asn1_class
|
166
150
|
return element.asn1_class unless @options.key?(:class)
|
167
151
|
|
@@ -169,6 +153,7 @@ module RASN1
|
|
169
153
|
end
|
170
154
|
|
171
155
|
# @return [Boolean]
|
156
|
+
# @see Types::Base#constructed
|
172
157
|
def constructed?
|
173
158
|
return element.constructed? unless @options.key?(:constructed)
|
174
159
|
|
@@ -176,10 +161,13 @@ module RASN1
|
|
176
161
|
end
|
177
162
|
|
178
163
|
# @return [Boolean]
|
164
|
+
# @see Types::Base#primitive
|
179
165
|
def primitive?
|
180
166
|
!constructed?
|
181
167
|
end
|
182
168
|
|
169
|
+
# @param [::Integer] level
|
170
|
+
# @return [String]
|
183
171
|
def inspect(level=0)
|
184
172
|
return super(level) unless explicit?
|
185
173
|
|
@@ -188,6 +176,26 @@ module RASN1
|
|
188
176
|
|
189
177
|
private
|
190
178
|
|
179
|
+
def explicit_implicit(options)
|
180
|
+
opts = options.dup
|
181
|
+
@explicit = opts.delete(:explicit)
|
182
|
+
@implicit = opts.delete(:implicit)
|
183
|
+
opts
|
184
|
+
end
|
185
|
+
|
186
|
+
def generate_explicit_wrapper(options)
|
187
|
+
# ExplicitWrapper is a hand-made explicit tag, but we have to use its implicit option
|
188
|
+
# to force its tag value.
|
189
|
+
@explicit_wrapper = ExplicitWrapper.new(options.merge(implicit: @explicit))
|
190
|
+
end
|
191
|
+
|
192
|
+
def generate_explicit_wrapper_options(options)
|
193
|
+
new_opts = {}
|
194
|
+
new_opts[:default] = options[:default] if options.key?(:default)
|
195
|
+
new_opts[:optional] = options[:optional] if options.key?(:optional)
|
196
|
+
new_opts
|
197
|
+
end
|
198
|
+
|
191
199
|
def generate_implicit_element
|
192
200
|
el = element.dup
|
193
201
|
if el.explicit?
|
data/lib/rasn1.rb
CHANGED
@@ -5,10 +5,14 @@ require_relative 'rasn1/errors'
|
|
5
5
|
require_relative 'rasn1/types'
|
6
6
|
require_relative 'rasn1/model'
|
7
7
|
require_relative 'rasn1/wrapper'
|
8
|
+
require_relative 'rasn1/tracer'
|
8
9
|
|
9
10
|
# Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
|
10
11
|
# @author Sylvain Daubert
|
11
12
|
module RASN1
|
13
|
+
# @private
|
14
|
+
CONTAINER_CLASSES = [Types::Sequence, Types::Set].freeze
|
15
|
+
|
12
16
|
# Parse a DER/BER string without checking a model
|
13
17
|
# @note If you want to check ASN.1 grammary, you should define a {Model}
|
14
18
|
# and use {Model#parse}.
|
@@ -19,24 +23,21 @@ module RASN1
|
|
19
23
|
# @param [String] der binary string to parse
|
20
24
|
# @param [Boolean] ber if +true+, decode a BER string, else a DER one
|
21
25
|
# @return [Types::Base]
|
22
|
-
def self.parse(der, ber: false)
|
23
|
-
|
24
|
-
|
25
|
-
type = Types.id2type(der)
|
26
|
-
type.parse!(der, ber: ber)
|
27
|
-
root ||= type
|
26
|
+
def self.parse(der, ber: false) # rubocop:disable Metrics:AbcSize
|
27
|
+
type = Types.id2type(der)
|
28
|
+
type.parse!(der, ber: ber)
|
28
29
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
type.value = ary
|
30
|
+
if CONTAINER_CLASSES.include?(type.class)
|
31
|
+
subder = type.value
|
32
|
+
ary = []
|
33
|
+
RASN1.tracer.tracing_level += 1 unless RASN1.tracer.nil?
|
34
|
+
until subder.empty?
|
35
|
+
ary << self.parse(subder)
|
36
|
+
subder = subder[ary.last.to_der.size..-1]
|
37
37
|
end
|
38
|
-
|
38
|
+
RASN1.tracer.tracing_level -= 1 unless RASN1.tracer.nil?
|
39
|
+
type.value = ary
|
39
40
|
end
|
40
|
-
|
41
|
+
type
|
41
42
|
end
|
42
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasn1
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-01-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: strptime
|
@@ -40,6 +40,7 @@ files:
|
|
40
40
|
- lib/rasn1.rb
|
41
41
|
- lib/rasn1/errors.rb
|
42
42
|
- lib/rasn1/model.rb
|
43
|
+
- lib/rasn1/tracer.rb
|
43
44
|
- lib/rasn1/types.rb
|
44
45
|
- lib/rasn1/types/any.rb
|
45
46
|
- lib/rasn1/types/base.rb
|
@@ -63,6 +64,7 @@ files:
|
|
63
64
|
- lib/rasn1/types/sequence_of.rb
|
64
65
|
- lib/rasn1/types/set.rb
|
65
66
|
- lib/rasn1/types/set_of.rb
|
67
|
+
- lib/rasn1/types/universal_string.rb
|
66
68
|
- lib/rasn1/types/utc_time.rb
|
67
69
|
- lib/rasn1/types/utf8_string.rb
|
68
70
|
- lib/rasn1/types/visible_string.rb
|
@@ -76,7 +78,7 @@ metadata:
|
|
76
78
|
source_code_uri: https://github.com/sdaubert/rasn1
|
77
79
|
bug_tracker_uri: https://github.com/sdaubert/rasn1/issues
|
78
80
|
documentation_uri: https://www.rubydoc.info/gems/rasn1
|
79
|
-
post_install_message:
|
81
|
+
post_install_message:
|
80
82
|
rdoc_options:
|
81
83
|
- "--title"
|
82
84
|
- RASN1 - A pure ruby ASN.1 library
|
@@ -90,15 +92,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
92
|
requirements:
|
91
93
|
- - ">="
|
92
94
|
- !ruby/object:Gem::Version
|
93
|
-
version: 2.
|
95
|
+
version: 2.7.0
|
94
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
95
97
|
requirements:
|
96
98
|
- - ">="
|
97
99
|
- !ruby/object:Gem::Version
|
98
100
|
version: '0'
|
99
101
|
requirements: []
|
100
|
-
rubygems_version: 3.
|
101
|
-
signing_key:
|
102
|
+
rubygems_version: 3.3.15
|
103
|
+
signing_key:
|
102
104
|
specification_version: 4
|
103
105
|
summary: Ruby ASN.1 library
|
104
106
|
test_files: []
|