rasn1 0.11.0 → 0.12.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/lib/rasn1/errors.rb +13 -2
- data/lib/rasn1/model.rb +152 -47
- data/lib/rasn1/types/any.rb +4 -0
- data/lib/rasn1/types/base.rb +35 -26
- data/lib/rasn1/types/bit_string.rb +11 -8
- data/lib/rasn1/types/bmp_string.rb +30 -0
- data/lib/rasn1/types/choice.rb +1 -1
- data/lib/rasn1/types/constrained.rb +1 -1
- data/lib/rasn1/types/constructed.rb +17 -1
- data/lib/rasn1/types/generalized_time.rb +29 -42
- data/lib/rasn1/types/ia5string.rb +2 -2
- data/lib/rasn1/types/integer.rb +2 -1
- data/lib/rasn1/types/null.rb +4 -0
- data/lib/rasn1/types/numeric_string.rb +1 -1
- data/lib/rasn1/types/printable_string.rb +1 -1
- data/lib/rasn1/types/sequence_of.rb +1 -1
- data/lib/rasn1/types/utc_time.rb +3 -7
- data/lib/rasn1/types/utf8_string.rb +2 -2
- data/lib/rasn1/types.rb +24 -12
- data/lib/rasn1/version.rb +1 -1
- data/lib/rasn1/wrapper.rb +201 -0
- data/lib/rasn1.rb +5 -4
- metadata +8 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 552859c3f49caec5374c8db4459ad23e1f9c43e930b0d6511cab8f997c798d20
|
4
|
+
data.tar.gz: 29eaf2fc529b470c9bc6283efbaf82e580d5b4b86d5c46920ee4a882a18b0d72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9e5df0e417db7e1f02accae9cc4f17b4a49975d1dc17b716dbf587bb3f5e8ca68ed842d22358d7de037041255c164df5a92c6ada52adba944f0ac635793a64b1
|
7
|
+
data.tar.gz: e193284ea13ca5f80a460b8b390839a0283a8424ca8201f24cbd6113919399fcd5b83a2fc39dd5b26925187e19930a1aceccb1485255de8301435e20a2c82761
|
data/lib/rasn1/errors.rb
CHANGED
@@ -20,8 +20,14 @@ module RASN1
|
|
20
20
|
|
21
21
|
# CHOICE error: #chosen not set
|
22
22
|
class ChoiceError < RASN1::Error
|
23
|
+
# @param [Types::Base] object
|
24
|
+
def initialize(object)
|
25
|
+
@object = object
|
26
|
+
super()
|
27
|
+
end
|
28
|
+
|
23
29
|
def message
|
24
|
-
"CHOICE #{@name}: #chosen not set"
|
30
|
+
"CHOICE #{@object.name}: #chosen not set"
|
25
31
|
end
|
26
32
|
end
|
27
33
|
|
@@ -37,7 +43,12 @@ module RASN1
|
|
37
43
|
|
38
44
|
# @return [String]
|
39
45
|
def message
|
40
|
-
"Constraint not verified on #{object.inspect}"
|
46
|
+
"Constraint not verified on #{@object.inspect}"
|
41
47
|
end
|
42
48
|
end
|
49
|
+
|
50
|
+
# Exception raised when model validation fails
|
51
|
+
# @since 0.12.0
|
52
|
+
class ModelValidationError < Error
|
53
|
+
end
|
43
54
|
end
|
data/lib/rasn1/model.rb
CHANGED
@@ -57,16 +57,42 @@ module RASN1
|
|
57
57
|
#
|
58
58
|
# == Delegation
|
59
59
|
# {Model} may delegate some methods to its root element. Thus, if root element
|
60
|
-
# is, for example, a {
|
60
|
+
# is, for example, a {Types::Choice}, model may delegate +#chosen+ and +#chosen_value+.
|
61
61
|
#
|
62
62
|
# All methods defined by root may be delegated by model, unless model also defines
|
63
63
|
# this method.
|
64
64
|
# @author Sylvain Daubert
|
65
|
-
class
|
65
|
+
# @author adfoster-r7 ModelValidationError, track source location for dynamic class methods
|
66
|
+
class Model # rubocop:disable Metrics/ClassLength
|
66
67
|
# @private
|
67
|
-
Elem = Struct.new(:name, :proc_or_class, :content)
|
68
|
+
Elem = Struct.new(:name, :proc_or_class, :content) do
|
69
|
+
def initialize(name, proc_or_class, content)
|
70
|
+
if content.is_a?(Array)
|
71
|
+
duplicate_names = find_all_duplicate_names(content.map(&:name) + [name])
|
72
|
+
raise ModelValidationError, "Duplicate name #{duplicate_names.first} found" if duplicate_names.any?
|
73
|
+
end
|
74
|
+
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# @param [Array<String>] names
|
81
|
+
# @return [Array<String>] The duplicate names found in the array
|
82
|
+
def find_all_duplicate_names(names)
|
83
|
+
names.group_by { |name| name }
|
84
|
+
.select { |_name, values| values.length > 1 }
|
85
|
+
.keys
|
86
|
+
end
|
87
|
+
end
|
68
88
|
|
69
89
|
# @private
|
90
|
+
WrapElem = Struct.new(:element, :options) do
|
91
|
+
def name
|
92
|
+
"#{element.name}_wrapper"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
70
96
|
module Accel
|
71
97
|
# @return [Hash]
|
72
98
|
attr_reader :options
|
@@ -79,6 +105,15 @@ module RASN1
|
|
79
105
|
@root = Elem.new(name, model_klass, nil)
|
80
106
|
end
|
81
107
|
|
108
|
+
# Use a {Wrapper} around a {Types::Base} or a {Model} object
|
109
|
+
# @param [Types::Base,Model] element
|
110
|
+
# @param [Hash] options
|
111
|
+
# @return [WrapElem]
|
112
|
+
# @since 0.12
|
113
|
+
def wrapper(element, options={})
|
114
|
+
@root = WrapElem.new(element, options)
|
115
|
+
end
|
116
|
+
|
82
117
|
# Update options of root element.
|
83
118
|
# May be used when subclassing.
|
84
119
|
# class Model1 < RASN1::Model
|
@@ -92,8 +127,13 @@ module RASN1
|
|
92
127
|
# end
|
93
128
|
# @param [Hash] options
|
94
129
|
# @return [void]
|
130
|
+
# @since 0.12.0 may change name through +:name+
|
95
131
|
def root_options(options)
|
96
132
|
@options = options
|
133
|
+
return unless options.key?(:name)
|
134
|
+
|
135
|
+
@root = @root.dup
|
136
|
+
@root.name = options[:name]
|
97
137
|
end
|
98
138
|
|
99
139
|
# On inheritance, create +@root+ class variable
|
@@ -105,33 +145,38 @@ module RASN1
|
|
105
145
|
klass.class_eval { @root = root }
|
106
146
|
end
|
107
147
|
|
148
|
+
# @since 0.11.0
|
149
|
+
# @since 0.12.0 track source location on error (adfoster-r7)
|
108
150
|
def define_type_accel_base(accel_name, klass)
|
109
|
-
singleton_class.class_eval
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
151
|
+
singleton_class.class_eval <<-EVAL, __FILE__, __LINE__ + 1
|
152
|
+
def #{accel_name}(name, options={}) # def sequence(name, type, options)
|
153
|
+
options[:name] = name
|
154
|
+
proc = proc do |opts|
|
155
|
+
#{klass}.new(options.merge(opts)) # Sequence.new(options.merge(opts))
|
156
|
+
end
|
157
|
+
@root = Elem.new(name, proc, options[:content])
|
158
|
+
end
|
159
|
+
EVAL
|
118
160
|
end
|
119
161
|
|
162
|
+
# @since 0.11.0
|
163
|
+
# @since 0.12.0 track source location on error (adfoster-r7)
|
120
164
|
def define_type_accel_of(accel_name, klass)
|
121
|
-
singleton_class.class_eval
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
165
|
+
singleton_class.class_eval <<-EVAL, __FILE__, __LINE__ + 1
|
166
|
+
def #{accel_name}_of(name, type, options={}) # def sequence_of(name, type, options)
|
167
|
+
options[:name] = name
|
168
|
+
proc = proc do |opts|
|
169
|
+
#{klass}.new(type, options.merge(opts)) # SequenceOf.new(type, options.merge(opts))
|
170
|
+
end
|
171
|
+
@root = Elem.new(name, proc, nil)
|
172
|
+
end
|
173
|
+
EVAL
|
130
174
|
end
|
131
175
|
|
132
176
|
# Define an accelarator to access a type in a model definition
|
133
177
|
# @param [String] accel_name
|
134
178
|
# @param [Class] klass
|
179
|
+
# @since 0.11.0
|
135
180
|
def define_type_accel(accel_name, klass)
|
136
181
|
if klass < Types::SequenceOf
|
137
182
|
define_type_accel_of(accel_name, klass)
|
@@ -184,17 +229,20 @@ module RASN1
|
|
184
229
|
|
185
230
|
extend Accel
|
186
231
|
|
187
|
-
#
|
232
|
+
# @!method sequence(name, options)
|
233
|
+
# @!scope class
|
188
234
|
# @param [Symbol,String] name name of object in model
|
189
235
|
# @param [Hash] options
|
190
236
|
# @return [Elem]
|
191
237
|
# @see Types::Sequence#initialize
|
192
|
-
#
|
238
|
+
# @!method set(name, options)
|
239
|
+
# @!scope class
|
193
240
|
# @param [Symbol,String] name name of object in model
|
194
241
|
# @param [Hash] options
|
195
242
|
# @return [Elem]
|
196
243
|
# @see Types::Set#initialize
|
197
|
-
#
|
244
|
+
# @!method choice(name, options)
|
245
|
+
# @!scope class
|
198
246
|
# @param [Symbol,String] name name of object in model
|
199
247
|
# @param [Hash] options
|
200
248
|
# @return [Elem]
|
@@ -203,13 +251,15 @@ module RASN1
|
|
203
251
|
self.define_type_accel_base(type, Types.const_get(type.capitalize))
|
204
252
|
end
|
205
253
|
|
206
|
-
#
|
254
|
+
# @!method sequence_of(name, type, options)
|
255
|
+
# @!scope class
|
207
256
|
# @param [Symbol,String] name name of object in model
|
208
257
|
# @param [Model, Types::Base] type type for SEQUENCE OF
|
209
258
|
# @param [Hash] options
|
210
259
|
# @return [Elem]
|
211
260
|
# @see Types::SequenceOf#initialize
|
212
|
-
#
|
261
|
+
# @!method set_of(name, type, options)
|
262
|
+
# @!scope class
|
213
263
|
# @param [Symbol,String] name name of object in model
|
214
264
|
# @param [Model, Types::Base] type type for SET OF
|
215
265
|
# @param [Hash] options
|
@@ -219,57 +269,68 @@ module RASN1
|
|
219
269
|
define_type_accel_of(type, Types.const_get("#{type.capitalize}Of"))
|
220
270
|
end
|
221
271
|
|
222
|
-
#
|
272
|
+
# @!method boolean(name, options)
|
273
|
+
# @!scope class
|
223
274
|
# @param [Symbol,String] name name of object in model
|
224
275
|
# @param [Hash] options
|
225
276
|
# @return [Elem]
|
226
277
|
# @see Types::Boolean#initialize
|
227
|
-
#
|
278
|
+
# @!method integer(name, options)
|
279
|
+
# @!scope class
|
228
280
|
# @param [Symbol,String] name name of object in model
|
229
281
|
# @param [Hash] options
|
230
282
|
# @return [Elem]
|
231
283
|
# @see Types::Integer#initialize
|
232
|
-
#
|
284
|
+
# @!method bit_string(name, options)
|
285
|
+
# @!scope class
|
233
286
|
# @param [Symbol,String] name name of object in model
|
234
287
|
# @param [Hash] options
|
235
288
|
# @return [Elem]
|
236
289
|
# @see Types::BitString#initialize
|
237
|
-
#
|
290
|
+
# @!method octet_string(name, options)
|
291
|
+
# @!scope class
|
238
292
|
# @param [Symbol,String] name name of object in model
|
239
293
|
# @param [Hash] options
|
240
294
|
# @return [Elem]
|
241
295
|
# @see Types::OctetString#initialize
|
242
|
-
#
|
296
|
+
# @!method null(name, options)
|
297
|
+
# @!scope class
|
243
298
|
# @param [Symbol,String] name name of object in model
|
244
299
|
# @param [Hash] options
|
245
300
|
# @return [Elem]
|
246
301
|
# @see Types::Null#initialize
|
247
|
-
#
|
302
|
+
# @!method enumerated(name, options)
|
303
|
+
# @!scope class
|
248
304
|
# @param [Symbol,String] name name of object in model
|
249
305
|
# @param [Hash] options
|
250
306
|
# @return [Elem]
|
251
307
|
# @see Types::Enumerated#initialize
|
252
|
-
#
|
308
|
+
# @!method utf8_string(name, options)
|
309
|
+
# @!scope class
|
253
310
|
# @param [Symbol,String] name name of object in model
|
254
311
|
# @param [Hash] options
|
255
312
|
# @return [Elem]
|
256
313
|
# @see Types::Utf8String#initialize
|
257
|
-
#
|
314
|
+
# @!method numeric_string(name, options)
|
315
|
+
# @!scope class
|
258
316
|
# @param [Symbol,String] name name of object in model
|
259
317
|
# @param [Hash] options
|
260
318
|
# @return [Elem]
|
261
319
|
# @see Types::NumericString#initialize
|
262
|
-
#
|
320
|
+
# @!method printable_string(name, options)
|
321
|
+
# @!scope class
|
263
322
|
# @param [Symbol,String] name name of object in model
|
264
323
|
# @param [Hash] options
|
265
324
|
# @return [Elem]
|
266
325
|
# @see Types::PrintableString#initialize
|
267
|
-
#
|
326
|
+
# @!method visible_string(name, options)
|
327
|
+
# @!scope class
|
268
328
|
# @param [Symbol,String] name name of object in model
|
269
329
|
# @param [Hash] options
|
270
330
|
# @return [Elem]
|
271
331
|
# @see Types::VisibleString#initialize
|
272
|
-
#
|
332
|
+
# @!method ia5_string(name, options)
|
333
|
+
# @!scope class
|
273
334
|
# @param [Symbol,String] name name of object in model
|
274
335
|
# @param [Hash] options
|
275
336
|
# @return [Elem]
|
@@ -285,7 +346,7 @@ module RASN1
|
|
285
346
|
# @param [Hash] args
|
286
347
|
def initialize(args={})
|
287
348
|
root = generate_root
|
288
|
-
|
349
|
+
generate_elements(root)
|
289
350
|
initialize_elements(self, args)
|
290
351
|
end
|
291
352
|
|
@@ -296,7 +357,7 @@ module RASN1
|
|
296
357
|
@elements[name]
|
297
358
|
end
|
298
359
|
|
299
|
-
# Set value of element +name+. Element should be a {Base}.
|
360
|
+
# Set value of element +name+. Element should be a {Types::Base}.
|
300
361
|
# @param [String,Symbol] name
|
301
362
|
# @param [Object] value
|
302
363
|
# @return [Object] value
|
@@ -460,14 +521,33 @@ module RASN1
|
|
460
521
|
class_element
|
461
522
|
end
|
462
523
|
|
463
|
-
def
|
524
|
+
def generate_elements(element)
|
525
|
+
if element.is_a?(WrapElem)
|
526
|
+
generate_wrapper(element)
|
527
|
+
return
|
528
|
+
end
|
464
529
|
return unless element.content.is_a? Array
|
465
530
|
|
466
531
|
@elements[name].value = element.content.map do |another_element|
|
467
|
-
|
468
|
-
|
469
|
-
|
532
|
+
add_subelement(another_element)
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
def generate_wrapper(wrap_elem)
|
537
|
+
inner_elem = wrap_elem.element
|
538
|
+
subel = add_subelement(inner_elem)
|
539
|
+
Wrapper.new(subel, wrap_elem.options)
|
540
|
+
end
|
541
|
+
|
542
|
+
def add_subelement(subelement)
|
543
|
+
case subelement
|
544
|
+
when Elem
|
545
|
+
subel = get_type(subelement.proc_or_class)
|
546
|
+
@elements[subelement.name] = subel
|
547
|
+
generate_elements(subelement) if composed?(subel) && subelement.content.is_a?(Array)
|
470
548
|
subel
|
549
|
+
when WrapElem
|
550
|
+
generate_wrapper(subelement)
|
471
551
|
end
|
472
552
|
end
|
473
553
|
|
@@ -477,7 +557,7 @@ module RASN1
|
|
477
557
|
|
478
558
|
case value
|
479
559
|
when Hash
|
480
|
-
raise ArgumentError, "element #{name}: may only pass a Hash for Model elements" unless obj[name].is_a?
|
560
|
+
raise ArgumentError, "element #{name}: may only pass a Hash for Model elements" unless obj[name].is_a?(Model)
|
481
561
|
|
482
562
|
initialize_elements obj[name], value
|
483
563
|
when Array
|
@@ -499,7 +579,7 @@ module RASN1
|
|
499
579
|
end
|
500
580
|
end
|
501
581
|
|
502
|
-
def private_to_h(element=nil)
|
582
|
+
def private_to_h(element=nil) # rubocop:disable Metrics/CyclomaticComplexity
|
503
583
|
my_element = element || root
|
504
584
|
my_element = my_element.root if my_element.is_a?(Model)
|
505
585
|
value = case my_element
|
@@ -507,6 +587,13 @@ module RASN1
|
|
507
587
|
sequence_of_to_h(my_element)
|
508
588
|
when Types::Sequence
|
509
589
|
sequence_to_h(my_element)
|
590
|
+
# @author adfoster-r7
|
591
|
+
when Types::Choice
|
592
|
+
raise ChoiceError.new(my_element) if my_element.chosen.nil?
|
593
|
+
|
594
|
+
private_to_h(my_element.value[my_element.chosen])
|
595
|
+
when Wrapper
|
596
|
+
wrapper_to_h(my_element)
|
510
597
|
else
|
511
598
|
my_element.value
|
512
599
|
end
|
@@ -529,11 +616,29 @@ module RASN1
|
|
529
616
|
ary = seq.value.map do |el|
|
530
617
|
next if el.optional? && el.value.nil?
|
531
618
|
|
532
|
-
|
533
|
-
|
619
|
+
case el
|
620
|
+
when Model
|
621
|
+
hsh = el.to_h
|
622
|
+
hsh = hsh[hsh.keys.first]
|
623
|
+
[@elements.key(el), hsh]
|
624
|
+
when Wrapper
|
625
|
+
[@elements.key(el.element), wrapper_to_h(el)]
|
626
|
+
else
|
627
|
+
[el.name, private_to_h(el)]
|
628
|
+
end
|
534
629
|
end
|
535
630
|
ary.compact!
|
536
631
|
ary.to_h
|
537
632
|
end
|
633
|
+
|
634
|
+
def wrapper_to_h(wrap)
|
635
|
+
case wrap.element
|
636
|
+
when Model
|
637
|
+
hsh = wrap.element.to_h
|
638
|
+
hsh[hsh.keys.first]
|
639
|
+
else
|
640
|
+
private_to_h(wrap.element)
|
641
|
+
end
|
642
|
+
end
|
538
643
|
end
|
539
644
|
end
|
data/lib/rasn1/types/any.rb
CHANGED
data/lib/rasn1/types/base.rb
CHANGED
@@ -69,6 +69,8 @@ module RASN1
|
|
69
69
|
attr_reader :asn1_class
|
70
70
|
# @return [Object,nil] default value, if defined
|
71
71
|
attr_reader :default
|
72
|
+
# @return [Hash[Symbol, Object]]
|
73
|
+
attr_reader :options
|
72
74
|
|
73
75
|
# Get ASN.1 type
|
74
76
|
# @return [String]
|
@@ -116,7 +118,8 @@ module RASN1
|
|
116
118
|
# @option options [::String] :name name for this node
|
117
119
|
def initialize(options={})
|
118
120
|
@constructed = nil
|
119
|
-
|
121
|
+
set_value(options.delete(:value))
|
122
|
+
self.options = options
|
120
123
|
specific_initializer
|
121
124
|
end
|
122
125
|
|
@@ -253,11 +256,38 @@ module RASN1
|
|
253
256
|
(other.class == self.class) && (other.to_der == self.to_der)
|
254
257
|
end
|
255
258
|
|
259
|
+
# Set options to this object
|
260
|
+
# @param [Hash] options
|
261
|
+
# @return [void]
|
262
|
+
# @since 0.12
|
263
|
+
def options=(options)
|
264
|
+
set_class options[:class]
|
265
|
+
set_optional options[:optional]
|
266
|
+
set_default options[:default]
|
267
|
+
set_tag options
|
268
|
+
@name = options[:name]
|
269
|
+
@options = options
|
270
|
+
end
|
271
|
+
|
272
|
+
# Say if a value is set
|
273
|
+
# @return [Boolean]
|
274
|
+
# @since 0.12.0
|
275
|
+
def value?
|
276
|
+
!@no_value
|
277
|
+
end
|
278
|
+
|
279
|
+
# Say if DER can be built (not default value, not optional without value, has a value)
|
280
|
+
# @return [Boolean]
|
281
|
+
# @since 0.12.0
|
282
|
+
def can_build?
|
283
|
+
value? && (@default.nil? || (@value != @default))
|
284
|
+
end
|
285
|
+
|
256
286
|
private
|
257
287
|
|
258
288
|
def pc_bit
|
259
289
|
if @constructed.nil?
|
260
|
-
self.class
|
290
|
+
self.class.const_get(:ASN1_PC)
|
261
291
|
elsif @constructed # true
|
262
292
|
Constructed::ASN1_PC
|
263
293
|
else # false
|
@@ -296,16 +326,6 @@ module RASN1
|
|
296
326
|
@value = der
|
297
327
|
end
|
298
328
|
|
299
|
-
def set_options(options) # rubocop:disable Naming/AccessorMethodName
|
300
|
-
set_class options[:class]
|
301
|
-
set_optional options[:optional]
|
302
|
-
set_default options[:default]
|
303
|
-
set_tag options
|
304
|
-
set_value options[:value]
|
305
|
-
@name = options[:name]
|
306
|
-
@options = options
|
307
|
-
end
|
308
|
-
|
309
329
|
def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
|
310
330
|
case asn1_class
|
311
331
|
when nil
|
@@ -330,17 +350,15 @@ module RASN1
|
|
330
350
|
# handle undocumented option +:tag_value+, used internally by
|
331
351
|
# {RASN1.parse} to parse non-universal class tags.
|
332
352
|
def set_tag(options) # rubocop:disable Naming/AccessorMethodName
|
353
|
+
@constructed = options[:constructed]
|
333
354
|
if options[:explicit]
|
334
355
|
@tag = :explicit
|
335
356
|
@id_value = options[:explicit]
|
336
|
-
@constructed = options[:constructed]
|
337
357
|
elsif options[:implicit]
|
338
358
|
@tag = :implicit
|
339
359
|
@id_value = options[:implicit]
|
340
|
-
@constructed = options[:constructed]
|
341
360
|
elsif options[:tag_value]
|
342
361
|
@id_value = options[:tag_value]
|
343
|
-
@constructed = options[:constructed]
|
344
362
|
end
|
345
363
|
|
346
364
|
@asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
|
@@ -357,15 +375,6 @@ module RASN1
|
|
357
375
|
value
|
358
376
|
end
|
359
377
|
|
360
|
-
def value?
|
361
|
-
!@no_value
|
362
|
-
end
|
363
|
-
|
364
|
-
def can_build?
|
365
|
-
(@default.nil? || (value? && (@value != @default))) &&
|
366
|
-
(!optional? || value?)
|
367
|
-
end
|
368
|
-
|
369
378
|
def build
|
370
379
|
if can_build?
|
371
380
|
if explicit?
|
@@ -382,9 +391,9 @@ module RASN1
|
|
382
391
|
end
|
383
392
|
|
384
393
|
def id_value
|
385
|
-
return @id_value if defined?
|
394
|
+
return @id_value if defined?(@id_value) && !@id_value.nil?
|
386
395
|
|
387
|
-
self.class
|
396
|
+
self.class.const_get(:ID)
|
388
397
|
end
|
389
398
|
|
390
399
|
def encode_identifier_octets
|
@@ -42,14 +42,14 @@ module RASN1
|
|
42
42
|
str << " #{value.inspect} (bit length: #{bit_length})"
|
43
43
|
end
|
44
44
|
|
45
|
-
private
|
46
|
-
|
47
45
|
def can_build?
|
48
|
-
|
49
|
-
(@bit_length == @default_bit_length))) &&
|
50
|
-
!(optional? && !value?)
|
46
|
+
super || (!@default.nil? && (@bit_length != @default_bit_length))
|
51
47
|
end
|
52
48
|
|
49
|
+
private
|
50
|
+
|
51
|
+
# @author Sylvain Daubert
|
52
|
+
# @author adfoster-r7
|
53
53
|
def value_to_der
|
54
54
|
raise ASN1Error, "#{@name}: bit length is not set" if bit_length.nil?
|
55
55
|
|
@@ -70,9 +70,8 @@ module RASN1
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def generate_value_with_correct_length
|
73
|
-
value = @value || ''
|
74
|
-
value << "\x00" while value.length * 8 < @bit_length.to_i
|
75
|
-
value.force_encoding('BINARY')
|
73
|
+
value = (@value || '').dup.force_encoding('BINARY')
|
74
|
+
value << "\x00".b while value.length * 8 < @bit_length.to_i
|
76
75
|
return value unless value.length * 8 > @bit_length.to_i
|
77
76
|
|
78
77
|
max_len = @bit_length.to_i / 8 + ((@bit_length.to_i % 8).positive? ? 1 : 0)
|
@@ -85,6 +84,10 @@ module RASN1
|
|
85
84
|
@bit_length = value.length * 8 - unused
|
86
85
|
@value = value
|
87
86
|
end
|
87
|
+
|
88
|
+
def explicit_type
|
89
|
+
self.class.new(value: @value, bit_length: @bit_length)
|
90
|
+
end
|
88
91
|
end
|
89
92
|
end
|
90
93
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RASN1
|
4
|
+
module Types
|
5
|
+
# ASN.1 BmpString
|
6
|
+
# @since 0.12.0
|
7
|
+
# @author adfoster-r7
|
8
|
+
class BmpString < OctetString
|
9
|
+
# BmpString id value
|
10
|
+
ID = 30
|
11
|
+
|
12
|
+
# Get ASN.1 type
|
13
|
+
# @return [String]
|
14
|
+
def self.type
|
15
|
+
'BmpString'
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def value_to_der
|
21
|
+
@value.to_s.dup.encode('UTF-16BE').b
|
22
|
+
end
|
23
|
+
|
24
|
+
def der_to_value(der, ber: false)
|
25
|
+
super
|
26
|
+
@value = der.to_s.dup.force_encoding('UTF-16BE')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/rasn1/types/choice.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
module RASN1
|
4
4
|
module Types
|
5
5
|
# Mixin to had constraints on a RASN1 type.
|
6
|
-
# Should not be used directly but through {
|
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,6 +10,22 @@ module RASN1
|
|
10
10
|
# Constructed value
|
11
11
|
ASN1_PC = 0x20
|
12
12
|
|
13
|
+
# @return [Boolean]
|
14
|
+
# @since 0.12.0
|
15
|
+
# @see Base#can_build?
|
16
|
+
def can_build? # rubocop:disable Metrics/CyclomaticComplexity
|
17
|
+
return super unless @value.is_a?(Array) && optional?
|
18
|
+
return false unless super
|
19
|
+
|
20
|
+
@value.any? do |el|
|
21
|
+
el.can_build? && (
|
22
|
+
el.primitive? ||
|
23
|
+
(el.value.respond_to?(:empty?) ? !el.value.empty? : !el.value.nil?))
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# @param [::Integer] level (default: 0)
|
28
|
+
# @return [String]
|
13
29
|
def inspect(level=0)
|
14
30
|
case @value
|
15
31
|
when Array
|
@@ -18,7 +34,7 @@ module RASN1
|
|
18
34
|
level = level.abs + 1
|
19
35
|
@value.each do |item|
|
20
36
|
case item
|
21
|
-
when Base, Model
|
37
|
+
when Base, Model, Wrapper
|
22
38
|
str << "#{item.inspect(level)}\n"
|
23
39
|
else
|
24
40
|
str << ' ' * level
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'strptime'
|
4
|
+
|
3
5
|
module RASN1
|
4
6
|
module Types
|
5
7
|
# ASN.1 GeneralizedTime
|
@@ -24,25 +26,33 @@ module RASN1
|
|
24
26
|
# GeneralizedTime id value
|
25
27
|
ID = 24
|
26
28
|
|
29
|
+
# @private
|
30
|
+
HOUR_TO_SEC = 3600
|
31
|
+
# @private
|
32
|
+
MINUTE_TO_SEC = 60
|
33
|
+
# @private
|
34
|
+
SECOND_TO_SEC = 1
|
35
|
+
|
27
36
|
# Get ASN.1 type
|
28
37
|
# @return [String]
|
29
38
|
def self.type
|
30
39
|
'GeneralizedTime'
|
31
40
|
end
|
32
41
|
|
33
|
-
# @return [
|
42
|
+
# @return [Time]
|
34
43
|
def void_value
|
35
|
-
|
44
|
+
Time.now
|
36
45
|
end
|
37
46
|
|
38
47
|
private
|
39
48
|
|
40
49
|
def value_to_der
|
41
|
-
|
42
|
-
|
50
|
+
utc_value = @value.getutc
|
51
|
+
if utc_value.nsec.positive?
|
52
|
+
der = utc_value.strftime('%Y%m%d%H%M%S.%9NZ')
|
43
53
|
der.sub(/0+Z/, 'Z')
|
44
54
|
else
|
45
|
-
|
55
|
+
utc_value.strftime('%Y%m%d%H%M%SZ')
|
46
56
|
end
|
47
57
|
end
|
48
58
|
|
@@ -61,18 +71,13 @@ module RASN1
|
|
61
71
|
end
|
62
72
|
|
63
73
|
def value_when_fraction_empty(date_hour)
|
64
|
-
utc_offset_forced = false
|
65
|
-
|
66
74
|
if (date_hour[-1] != 'Z') && (date_hour !~ /[+-]\d+$/)
|
67
75
|
# If not UTC, have to add offset with UTC to force
|
68
|
-
#
|
69
|
-
# may be errored because of DST.
|
76
|
+
# Strptime to generate a local time.
|
70
77
|
date_hour << Time.now.strftime('%z')
|
71
|
-
utc_offset_forced = true
|
72
78
|
end
|
73
79
|
|
74
80
|
value_from(date_hour)
|
75
|
-
fix_dst if utc_offset_forced
|
76
81
|
end
|
77
82
|
|
78
83
|
def value_when_fraction_ends_with_z(date_hour, fraction)
|
@@ -90,62 +95,44 @@ module RASN1
|
|
90
95
|
date_hour << match[2]
|
91
96
|
else
|
92
97
|
# fraction only contains fraction.
|
93
|
-
# Have to add offset with UTC to force
|
94
|
-
# generate a local time.
|
95
|
-
# because of DST.
|
98
|
+
# Have to add offset with UTC to force Strptime to
|
99
|
+
# generate a local time.
|
96
100
|
date_hour << Time.now.strftime('%z')
|
97
|
-
utc_offset_forced = true
|
98
101
|
end
|
99
102
|
|
100
103
|
frac_base = value_from(date_hour)
|
101
|
-
fix_dst if utc_offset_forced
|
102
104
|
fix_value(fraction, frac_base)
|
103
105
|
end
|
104
106
|
|
105
107
|
def value_from(date_hour)
|
106
108
|
format, frac_base = strformat(date_hour)
|
107
|
-
@value =
|
109
|
+
@value = Strptime.new(format).exec(date_hour)
|
108
110
|
frac_base
|
109
111
|
end
|
110
112
|
|
111
|
-
# Check DST. There may be a shift of one hour...
|
112
|
-
def fix_dst
|
113
|
-
compare_time = Time.new(*@value.to_a[0..5].reverse)
|
114
|
-
@value = compare_time if compare_time.utc_offset != @value.utc_offset
|
115
|
-
end
|
116
|
-
|
117
113
|
def fix_value(fraction, frac_base)
|
118
|
-
|
114
|
+
frac = ".#{fraction}".to_r * frac_base
|
115
|
+
@value = (@value + frac) unless fraction.nil?
|
119
116
|
end
|
120
117
|
|
121
|
-
def strformat(date_hour)
|
118
|
+
def strformat(date_hour)
|
122
119
|
case date_hour.size
|
123
120
|
when 11
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
frac_base = 60
|
128
|
-
format = '%Y%m%d%H%MZ'
|
121
|
+
['%Y%m%d%H%z', HOUR_TO_SEC]
|
122
|
+
when 13, 17
|
123
|
+
['%Y%m%d%H%M%z', MINUTE_TO_SEC]
|
129
124
|
when 15
|
130
125
|
if date_hour[-1] == 'Z'
|
131
|
-
|
132
|
-
format = '%Y%m%d%H%M%SZ'
|
126
|
+
['%Y%m%d%H%M%S%z', SECOND_TO_SEC]
|
133
127
|
else
|
134
|
-
|
135
|
-
format = '%Y%m%d%H%z'
|
128
|
+
['%Y%m%d%H%z', HOUR_TO_SEC]
|
136
129
|
end
|
137
|
-
when 17
|
138
|
-
frac_base = 60
|
139
|
-
format = '%Y%m%d%H%M%z'
|
140
130
|
when 19
|
141
|
-
|
142
|
-
format = '%Y%m%d%H%M%S%z'
|
131
|
+
['%Y%m%d%H%M%S%z', SECOND_TO_SEC]
|
143
132
|
else
|
144
133
|
prefix = @name.nil? ? type : "tag #{@name}"
|
145
|
-
raise ASN1Error, "#{prefix}: unrecognized format: #{
|
134
|
+
raise ASN1Error, "#{prefix}: unrecognized format: #{date_hour}"
|
146
135
|
end
|
147
|
-
|
148
|
-
[format, frac_base]
|
149
136
|
end
|
150
137
|
end
|
151
138
|
end
|
@@ -17,12 +17,12 @@ module RASN1
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def value_to_der
|
20
|
-
@value.to_s.force_encoding('US-ASCII').
|
20
|
+
@value.to_s.dup.force_encoding('US-ASCII').b
|
21
21
|
end
|
22
22
|
|
23
23
|
def der_to_value(der, ber: false)
|
24
24
|
super
|
25
|
-
@value.to_s.force_encoding('US-ASCII')
|
25
|
+
@value.to_s.dup.force_encoding('US-ASCII')
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/rasn1/types/integer.rb
CHANGED
@@ -133,8 +133,9 @@ module RASN1
|
|
133
133
|
return if @enum.empty?
|
134
134
|
|
135
135
|
int_value = @value
|
136
|
+
raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless @enum.value?(@value)
|
137
|
+
|
136
138
|
@value = @enum.key(@value)
|
137
|
-
raise EnumeratedError, "#{@name}: value #{int_value} not in enumeration" unless value?
|
138
139
|
end
|
139
140
|
|
140
141
|
def explicit_type
|
data/lib/rasn1/types/null.rb
CHANGED
@@ -59,7 +59,6 @@ module RASN1
|
|
59
59
|
Sequence.encoded_type
|
60
60
|
end
|
61
61
|
|
62
|
-
# @param [Symbol, String] name name for this tag in grammar
|
63
62
|
# @param [Class, Base] of_type base type for sequence of
|
64
63
|
# @see Base#initialize
|
65
64
|
def initialize(of_type, options={})
|
@@ -105,6 +104,7 @@ module RASN1
|
|
105
104
|
@value.length
|
106
105
|
end
|
107
106
|
|
107
|
+
# @return [String]
|
108
108
|
def inspect(level=0)
|
109
109
|
str = common_inspect(level)
|
110
110
|
str << "\n"
|
data/lib/rasn1/types/utc_time.rb
CHANGED
@@ -35,20 +35,16 @@ module RASN1
|
|
35
35
|
|
36
36
|
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
37
37
|
format = case der.size
|
38
|
-
when 11
|
39
|
-
'%Y%m%d%H%MZ'
|
40
|
-
when 13
|
41
|
-
'%Y%m%d%H%M%SZ'
|
42
|
-
when 15
|
38
|
+
when 11, 15
|
43
39
|
'%Y%m%d%H%M%z'
|
44
|
-
when 17
|
40
|
+
when 13, 17
|
45
41
|
'%Y%m%d%H%M%S%z'
|
46
42
|
else
|
47
43
|
prefix = @name.nil? ? type : "tag #{@name}"
|
48
44
|
raise ASN1Error, "#{prefix}: unrecognized format: #{der}"
|
49
45
|
end
|
50
46
|
century = (Time.now.year / 100).to_s
|
51
|
-
@value =
|
47
|
+
@value = Strptime.new(format).exec(century + der)
|
52
48
|
end
|
53
49
|
end
|
54
50
|
end
|
@@ -17,12 +17,12 @@ module RASN1
|
|
17
17
|
private
|
18
18
|
|
19
19
|
def value_to_der
|
20
|
-
@value.to_s.force_encoding('UTF-8').
|
20
|
+
@value.to_s.dup.force_encoding('UTF-8').b
|
21
21
|
end
|
22
22
|
|
23
23
|
def der_to_value(der, ber: false)
|
24
24
|
super
|
25
|
-
@value = der.force_encoding('UTF-8')
|
25
|
+
@value = der.dup.force_encoding('UTF-8')
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/rasn1/types.rb
CHANGED
@@ -4,18 +4,25 @@ module RASN1
|
|
4
4
|
# This modules is a namesapce for all ASN.1 type classes.
|
5
5
|
# @author Sylvain Daubert
|
6
6
|
module Types
|
7
|
+
@primitives = []
|
8
|
+
@constructed = []
|
9
|
+
|
7
10
|
# Give all primitive types
|
8
11
|
# @return [Array<Types::Primitive>]
|
9
12
|
def self.primitives
|
10
|
-
@primitives
|
11
|
-
|
13
|
+
return @primitives unless @primitives.empty?
|
14
|
+
|
15
|
+
@primitives = self.constants.map { |c| Types.const_get(c) }
|
16
|
+
.select { |klass| klass < Primitive }
|
12
17
|
end
|
13
18
|
|
14
19
|
# Give all constructed types
|
15
20
|
# @return [Array<Types::Constructed>]
|
16
21
|
def self.constructed
|
17
|
-
@constructed
|
18
|
-
|
22
|
+
return @constructed unless @constructed.empty?
|
23
|
+
|
24
|
+
@constructed = self.constants.map { |c| Types.const_get(c) }
|
25
|
+
.select { |klass| klass < Constructed }
|
19
26
|
end
|
20
27
|
|
21
28
|
# @private
|
@@ -67,8 +74,8 @@ module RASN1
|
|
67
74
|
def self.generate_id2type_cache
|
68
75
|
constructed = self.constructed - [Types::SequenceOf, Types::SetOf]
|
69
76
|
primitives = self.primitives - [Types::Enumerated]
|
70
|
-
ary = (primitives + constructed).select { |type| type.const_defined?
|
71
|
-
.map { |type| [type
|
77
|
+
ary = (primitives + constructed).select { |type| type.const_defined?(:ID) }
|
78
|
+
.map { |type| [type.const_get(:ID), type] }
|
72
79
|
@id2types = ary.to_h
|
73
80
|
@id2types.default = Types::Base
|
74
81
|
@id2types.freeze
|
@@ -77,9 +84,12 @@ module RASN1
|
|
77
84
|
# Define a new ASN.1 type from a base one.
|
78
85
|
# This new type may have a constraint defines on it.
|
79
86
|
# @param [Symbol,String] name New type name. Must start with a capital letter.
|
80
|
-
# @param [Types::Base] from
|
87
|
+
# @param [Types::Base] from class from which inherits
|
88
|
+
# @param [Module] in_module module in which creates new type (default to {RASN1::Types})
|
81
89
|
# @return [Class] newly created class
|
82
|
-
|
90
|
+
# @since 0.11.0
|
91
|
+
# @since 0.12.0 in_module parameter
|
92
|
+
def self.define_type(name, from:, in_module: self, &block)
|
83
93
|
constraint = block.nil? ? nil : block.to_proc
|
84
94
|
|
85
95
|
new_klass = Class.new(from) do
|
@@ -87,12 +97,13 @@ module RASN1
|
|
87
97
|
end
|
88
98
|
new_klass.constraint = constraint
|
89
99
|
|
90
|
-
|
91
|
-
|
100
|
+
in_module.const_set(name, new_klass)
|
101
|
+
accel_name = name.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase
|
102
|
+
Model.define_type_accel(accel_name, new_klass)
|
92
103
|
|
93
104
|
# Empty type caches
|
94
|
-
@primitives =
|
95
|
-
@constructed =
|
105
|
+
@primitives = []
|
106
|
+
@constructed = []
|
96
107
|
|
97
108
|
new_klass
|
98
109
|
end
|
@@ -108,6 +119,7 @@ require_relative 'types/octet_string'
|
|
108
119
|
require_relative 'types/null'
|
109
120
|
require_relative 'types/object_id'
|
110
121
|
require_relative 'types/enumerated'
|
122
|
+
require_relative 'types/bmp_string'
|
111
123
|
require_relative 'types/utf8_string'
|
112
124
|
require_relative 'types/numeric_string'
|
113
125
|
require_relative 'types/printable_string'
|
data/lib/rasn1/version.rb
CHANGED
@@ -0,0 +1,201 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module RASN1
|
6
|
+
# This class is used to wrap a {Types::Base} or {Model} instance to force its options.
|
7
|
+
#
|
8
|
+
# == Usage
|
9
|
+
# This class may be used to wrap another RASN1 object by 3 ways:
|
10
|
+
# * wrap an object to modify its options,
|
11
|
+
# * implicitly wrap an object (i.e. change its tag),
|
12
|
+
# * explicitly wrap an object (i.e wrap the object in another explicit ASN.1 tag)
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# # object to wrap
|
16
|
+
# int = RASN1::Types::Integer.new(implicit: 1) # its tag is 0x81
|
17
|
+
# # simple wraper, change an option
|
18
|
+
# wrapper = RASN1::Wrapper.new(int, optional: true, default: 1)
|
19
|
+
# # implicit wrapper
|
20
|
+
# wrapper = RASN1::Wrapper.new(int, implicit: 3) # wrapped int tag is now 0x83
|
21
|
+
# # explicit wrapper
|
22
|
+
# wrapper = RASN1::Wrapper.new(int, explicit: 4) # int tag is always 0x81, but it is wrapped in a 0x84 tag
|
23
|
+
# @since 0.12.0
|
24
|
+
class Wrapper < SimpleDelegator
|
25
|
+
# @private Private class used to build/parse explicit wrappers
|
26
|
+
class ExplicitWrapper < Types::Base
|
27
|
+
ID = 0 # not used
|
28
|
+
ASN1_PC = 0 # not constructed
|
29
|
+
|
30
|
+
def self.type
|
31
|
+
''
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Boolean]
|
35
|
+
# @see Types::Base#can_build?
|
36
|
+
def can_build?
|
37
|
+
ok = super
|
38
|
+
return ok unless optional?
|
39
|
+
|
40
|
+
ok && @value.can_build?
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def value_to_der
|
46
|
+
@value.is_a?(String) ? @value : @value.to_der
|
47
|
+
end
|
48
|
+
|
49
|
+
def inspect_value
|
50
|
+
''
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# @param [Types::Base,Model] element element to wrap
|
55
|
+
# @param [Hash] options
|
56
|
+
def initialize(element, options={})
|
57
|
+
opts = explicit_implicit(options)
|
58
|
+
|
59
|
+
if explicit?
|
60
|
+
generate_explicit_wrapper(opts)
|
61
|
+
element.options = element.options.merge(generate_explicit_wrapper_options(opts))
|
62
|
+
@options = opts
|
63
|
+
else
|
64
|
+
opts[:value] = element.value
|
65
|
+
element.options = element.options.merge(opts)
|
66
|
+
@options = {}
|
67
|
+
end
|
68
|
+
raise RASN1::Error, 'Cannot be implicit and explicit' if explicit? && implicit?
|
69
|
+
|
70
|
+
super(element)
|
71
|
+
end
|
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
|
+
# Say if wrapper is an explicit one (i.e. add tag and length to its element)
|
94
|
+
# @return [Boolean]
|
95
|
+
def explicit?
|
96
|
+
!!@explicit
|
97
|
+
end
|
98
|
+
|
99
|
+
# Say if wrapper is an implicit one (i.e. change tag of its element)
|
100
|
+
# @return [Boolean]
|
101
|
+
def implicit?
|
102
|
+
!!@implicit
|
103
|
+
end
|
104
|
+
|
105
|
+
# Convert wrapper and its element to a DER string
|
106
|
+
# @return [String]
|
107
|
+
def to_der
|
108
|
+
if implicit?
|
109
|
+
el = generate_implicit_element
|
110
|
+
el.to_der
|
111
|
+
elsif explicit?
|
112
|
+
@explicit_wrapper.value = element
|
113
|
+
@explicit_wrapper.to_der
|
114
|
+
else
|
115
|
+
element.to_der
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Parse a DER string. This method updates object.
|
120
|
+
# @param [String] der DER string
|
121
|
+
# @param [Boolean] ber if +true+, accept BER encoding
|
122
|
+
# @return [Integer] total number of parsed bytes
|
123
|
+
# @raise [ASN1Error] error on parsing
|
124
|
+
def parse!(der, ber: false)
|
125
|
+
if implicit?
|
126
|
+
el = generate_implicit_element
|
127
|
+
parsed = el.parse!(der, ber: ber)
|
128
|
+
element.value = el.value
|
129
|
+
parsed
|
130
|
+
elsif explicit?
|
131
|
+
parsed = @explicit_wrapper.parse!(der, ber: ber)
|
132
|
+
element.parse!(@explicit_wrapper.value, ber: ber) if parsed.positive?
|
133
|
+
parsed
|
134
|
+
else
|
135
|
+
element.parse!(der, ber: ber)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def value?
|
140
|
+
if explicit?
|
141
|
+
@explicit_wrapper.value?
|
142
|
+
else
|
143
|
+
__getobj__.value?
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Return Wrapped element
|
148
|
+
# @return [Types::Base,Model]
|
149
|
+
def element
|
150
|
+
__getobj__
|
151
|
+
end
|
152
|
+
|
153
|
+
# @return [::Integer]
|
154
|
+
def id
|
155
|
+
if implicit?
|
156
|
+
@implicit
|
157
|
+
elsif explicit?
|
158
|
+
@explicit
|
159
|
+
else
|
160
|
+
element.id
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# @return [Symbol]
|
165
|
+
def asn1_class
|
166
|
+
return element.asn1_class unless @options.key?(:class)
|
167
|
+
|
168
|
+
@options[:class]
|
169
|
+
end
|
170
|
+
|
171
|
+
# @return [Boolean]
|
172
|
+
def constructed?
|
173
|
+
return element.constructed? unless @options.key?(:constructed)
|
174
|
+
|
175
|
+
@options[:constructed]
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [Boolean]
|
179
|
+
def primitive?
|
180
|
+
!constructed?
|
181
|
+
end
|
182
|
+
|
183
|
+
def inspect(level=0)
|
184
|
+
return super(level) unless explicit?
|
185
|
+
|
186
|
+
@explicit_wrapper.inspect(level) << ' ' << super(level)
|
187
|
+
end
|
188
|
+
|
189
|
+
private
|
190
|
+
|
191
|
+
def generate_implicit_element
|
192
|
+
el = element.dup
|
193
|
+
if el.explicit?
|
194
|
+
el.options = el.options.merge(explicit: @implicit)
|
195
|
+
elsif el.implicit?
|
196
|
+
el.options = el.options.merge(implicit: @implicit)
|
197
|
+
end
|
198
|
+
el
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
data/lib/rasn1.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
3
|
+
require_relative 'rasn1/version'
|
4
|
+
require_relative 'rasn1/errors'
|
5
|
+
require_relative 'rasn1/types'
|
6
|
+
require_relative 'rasn1/model'
|
7
|
+
require_relative 'rasn1/wrapper'
|
7
8
|
|
8
9
|
# Rasn1 is a pure ruby library to parse, decode and encode ASN.1 data.
|
9
10
|
# @author Sylvain Daubert
|
metadata
CHANGED
@@ -1,57 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rasn1
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.12.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sylvain Daubert
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
14
|
+
name: strptime
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
20
|
-
type: :
|
19
|
+
version: 0.2.5
|
20
|
+
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version:
|
27
|
-
- !ruby/object:Gem::Dependency
|
28
|
-
name: rspec
|
29
|
-
requirement: !ruby/object:Gem::Requirement
|
30
|
-
requirements:
|
31
|
-
- - "~>"
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
version: '3.0'
|
34
|
-
type: :development
|
35
|
-
prerelease: false
|
36
|
-
version_requirements: !ruby/object:Gem::Requirement
|
37
|
-
requirements:
|
38
|
-
- - "~>"
|
39
|
-
- !ruby/object:Gem::Version
|
40
|
-
version: '3.0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: yard
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0.9'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0.9'
|
26
|
+
version: 0.2.5
|
55
27
|
description: |
|
56
28
|
RASN1 is a pure ruby ASN.1 library. It may encode and decode DER and BER
|
57
29
|
encodings.
|
@@ -72,6 +44,7 @@ files:
|
|
72
44
|
- lib/rasn1/types/any.rb
|
73
45
|
- lib/rasn1/types/base.rb
|
74
46
|
- lib/rasn1/types/bit_string.rb
|
47
|
+
- lib/rasn1/types/bmp_string.rb
|
75
48
|
- lib/rasn1/types/boolean.rb
|
76
49
|
- lib/rasn1/types/choice.rb
|
77
50
|
- lib/rasn1/types/constrained.rb
|
@@ -94,6 +67,7 @@ files:
|
|
94
67
|
- lib/rasn1/types/utf8_string.rb
|
95
68
|
- lib/rasn1/types/visible_string.rb
|
96
69
|
- lib/rasn1/version.rb
|
70
|
+
- lib/rasn1/wrapper.rb
|
97
71
|
homepage: https://github.com/sdaubert/rasn1
|
98
72
|
licenses:
|
99
73
|
- MIT
|