rasn1 0.12.0 → 0.13.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|