rasn1 0.11.0 → 0.12.1
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 +15 -3
- data/lib/rasn1/model.rb +165 -48
- data/lib/rasn1/tracer.rb +174 -0
- data/lib/rasn1/types/any.rb +38 -13
- data/lib/rasn1/types/base.rb +160 -48
- data/lib/rasn1/types/bit_string.rb +14 -9
- data/lib/rasn1/types/bmp_string.rb +30 -0
- data/lib/rasn1/types/boolean.rb +6 -0
- data/lib/rasn1/types/choice.rb +9 -1
- data/lib/rasn1/types/constrained.rb +7 -2
- data/lib/rasn1/types/constructed.rb +17 -1
- data/lib/rasn1/types/generalized_time.rb +34 -41
- data/lib/rasn1/types/ia5string.rb +2 -2
- data/lib/rasn1/types/integer.rb +17 -5
- data/lib/rasn1/types/null.rb +6 -0
- data/lib/rasn1/types/numeric_string.rb +1 -1
- data/lib/rasn1/types/octet_string.rb +2 -0
- data/lib/rasn1/types/printable_string.rb +1 -1
- data/lib/rasn1/types/sequence.rb +7 -2
- data/lib/rasn1/types/sequence_of.rb +9 -3
- data/lib/rasn1/types/utc_time.rb +9 -7
- data/lib/rasn1/types/utf8_string.rb +2 -2
- data/lib/rasn1/types.rb +32 -12
- data/lib/rasn1/version.rb +1 -1
- data/lib/rasn1/wrapper.rb +209 -0
- data/lib/rasn1.rb +22 -20
- metadata +9 -34
data/lib/rasn1/types/base.rb
CHANGED
@@ -69,6 +69,14 @@ 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
|
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
|
72
80
|
|
73
81
|
# Get ASN.1 type
|
74
82
|
# @return [String]
|
@@ -116,15 +124,19 @@ module RASN1
|
|
116
124
|
# @option options [::String] :name name for this node
|
117
125
|
def initialize(options={})
|
118
126
|
@constructed = nil
|
119
|
-
|
127
|
+
set_value(options.delete(:value))
|
128
|
+
self.options = options
|
120
129
|
specific_initializer
|
130
|
+
@raw_data = ''.b
|
131
|
+
@raw_length = ''.b
|
121
132
|
end
|
122
133
|
|
123
134
|
# @abstract To help subclass initialize itself. Default implementation do nothing.
|
124
135
|
def specific_initializer; end
|
125
136
|
|
126
|
-
#
|
127
|
-
def initialize_copy(
|
137
|
+
# Deep copy @value and @default.
|
138
|
+
def initialize_copy(*)
|
139
|
+
super
|
128
140
|
@value = @value.dup
|
129
141
|
@no_value = @no_value.dup
|
130
142
|
@default = @default.dup
|
@@ -150,6 +162,7 @@ module RASN1
|
|
150
162
|
''
|
151
163
|
end
|
152
164
|
|
165
|
+
# Say if this type is optional
|
153
166
|
# @return [::Boolean]
|
154
167
|
def optional?
|
155
168
|
@optional
|
@@ -212,17 +225,11 @@ module RASN1
|
|
212
225
|
# @return [Integer] total number of parsed bytes
|
213
226
|
# @raise [ASN1Error] error on parsing
|
214
227
|
def parse!(der, ber: false)
|
215
|
-
|
228
|
+
total_length, data = do_parse(der, ber)
|
229
|
+
return 0 if total_length.zero?
|
216
230
|
|
217
|
-
id_size = Types.decode_identifier_octets(der).last
|
218
|
-
total_length, data = get_data(der[id_size..-1], ber)
|
219
|
-
total_length += id_size
|
220
|
-
@no_value = false
|
221
231
|
if explicit?
|
222
|
-
|
223
|
-
type = explicit_type
|
224
|
-
type.parse!(data)
|
225
|
-
@value = type.value
|
232
|
+
do_parse_explicit(data)
|
226
233
|
else
|
227
234
|
der_to_value(data, ber: ber)
|
228
235
|
end
|
@@ -253,11 +260,112 @@ module RASN1
|
|
253
260
|
(other.class == self.class) && (other.to_der == self.to_der)
|
254
261
|
end
|
255
262
|
|
263
|
+
# Set options to this object
|
264
|
+
# @param [Hash] options
|
265
|
+
# @return [void]
|
266
|
+
# @since 0.12
|
267
|
+
def options=(options)
|
268
|
+
set_class options[:class]
|
269
|
+
set_optional options[:optional]
|
270
|
+
set_default options[:default]
|
271
|
+
set_tag options
|
272
|
+
@name = options[:name]
|
273
|
+
@options = options
|
274
|
+
end
|
275
|
+
|
276
|
+
# Say if a value is set
|
277
|
+
# @return [Boolean]
|
278
|
+
# @since 0.12.0
|
279
|
+
def value?
|
280
|
+
!@no_value
|
281
|
+
end
|
282
|
+
|
283
|
+
# Say if DER can be built (not default value, not optional without value, has a value)
|
284
|
+
# @return [Boolean]
|
285
|
+
# @since 0.12.0
|
286
|
+
def can_build?
|
287
|
+
value? && (@default.nil? || (@value != @default))
|
288
|
+
end
|
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
|
+
|
256
303
|
private
|
257
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
|
+
|
258
366
|
def pc_bit
|
259
367
|
if @constructed.nil?
|
260
|
-
self.class
|
368
|
+
self.class.const_get(:ASN1_PC)
|
261
369
|
elsif @constructed # true
|
262
370
|
Constructed::ASN1_PC
|
263
371
|
else # false
|
@@ -269,7 +377,7 @@ module RASN1
|
|
269
377
|
lvl = level >= 0 ? level : 0
|
270
378
|
str = ' ' * lvl
|
271
379
|
str << "#{@name} " unless @name.nil?
|
272
|
-
str <<
|
380
|
+
str << asn1_class_to_s
|
273
381
|
str << "[#{id}] EXPLICIT " if explicit?
|
274
382
|
str << "[#{id}] IMPLICIT " if implicit?
|
275
383
|
str << "#{type}:"
|
@@ -283,6 +391,24 @@ module RASN1
|
|
283
391
|
end
|
284
392
|
end
|
285
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
|
+
|
286
412
|
def value_to_der
|
287
413
|
case @value
|
288
414
|
when Base
|
@@ -296,16 +422,6 @@ module RASN1
|
|
296
422
|
@value = der
|
297
423
|
end
|
298
424
|
|
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
425
|
def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
|
310
426
|
case asn1_class
|
311
427
|
when nil
|
@@ -330,17 +446,15 @@ module RASN1
|
|
330
446
|
# handle undocumented option +:tag_value+, used internally by
|
331
447
|
# {RASN1.parse} to parse non-universal class tags.
|
332
448
|
def set_tag(options) # rubocop:disable Naming/AccessorMethodName
|
449
|
+
@constructed = options[:constructed]
|
333
450
|
if options[:explicit]
|
334
451
|
@tag = :explicit
|
335
452
|
@id_value = options[:explicit]
|
336
|
-
@constructed = options[:constructed]
|
337
453
|
elsif options[:implicit]
|
338
454
|
@tag = :implicit
|
339
455
|
@id_value = options[:implicit]
|
340
|
-
@constructed = options[:constructed]
|
341
456
|
elsif options[:tag_value]
|
342
457
|
@id_value = options[:tag_value]
|
343
|
-
@constructed = options[:constructed]
|
344
458
|
end
|
345
459
|
|
346
460
|
@asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
|
@@ -357,15 +471,6 @@ module RASN1
|
|
357
471
|
value
|
358
472
|
end
|
359
473
|
|
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
474
|
def build
|
370
475
|
if can_build?
|
371
476
|
if explicit?
|
@@ -382,9 +487,9 @@ module RASN1
|
|
382
487
|
end
|
383
488
|
|
384
489
|
def id_value
|
385
|
-
return @id_value if defined?
|
490
|
+
return @id_value if defined?(@id_value) && !@id_value.nil?
|
386
491
|
|
387
|
-
self.class
|
492
|
+
self.class.const_get(:ID)
|
388
493
|
end
|
389
494
|
|
390
495
|
def encode_identifier_octets
|
@@ -445,6 +550,21 @@ module RASN1
|
|
445
550
|
end
|
446
551
|
|
447
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)
|
448
568
|
length = der.unpack1('C').to_i
|
449
569
|
length_length = 0
|
450
570
|
|
@@ -455,12 +575,8 @@ module RASN1
|
|
455
575
|
length = der[1, length_length].unpack('C*')
|
456
576
|
.reduce(0) { |len, b| (len << 8) | b }
|
457
577
|
end
|
458
|
-
data = der[1 + length_length, length]
|
459
|
-
|
460
|
-
total_length = 1 + length
|
461
|
-
total_length += length_length if length_length.positive?
|
462
578
|
|
463
|
-
[
|
579
|
+
[length, length_length]
|
464
580
|
end
|
465
581
|
|
466
582
|
def raise_on_indefinite_length(ber)
|
@@ -475,7 +591,7 @@ module RASN1
|
|
475
591
|
end
|
476
592
|
|
477
593
|
def explicit_type
|
478
|
-
self.class.new
|
594
|
+
self.class.new(name: name)
|
479
595
|
end
|
480
596
|
|
481
597
|
def raise_id_error(der)
|
@@ -484,10 +600,6 @@ module RASN1
|
|
484
600
|
raise ASN1Error, msg
|
485
601
|
end
|
486
602
|
|
487
|
-
def class_from_numeric_id(id)
|
488
|
-
CLASSES.key(id & CLASS_MASK)
|
489
|
-
end
|
490
|
-
|
491
603
|
def self2name
|
492
604
|
name = +"#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
|
493
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,14 +41,17 @@ module RASN1
|
|
42
41
|
str << " #{value.inspect} (bit length: #{bit_length})"
|
43
42
|
end
|
44
43
|
|
45
|
-
|
46
|
-
|
44
|
+
# Same as {Base#can_build?} but also check bit_length
|
45
|
+
# @see Base#can_build?
|
46
|
+
# @return [Boolean]
|
47
47
|
def can_build?
|
48
|
-
|
49
|
-
(@bit_length == @default_bit_length))) &&
|
50
|
-
!(optional? && !value?)
|
48
|
+
super || (!@default.nil? && (@bit_length != @default_bit_length))
|
51
49
|
end
|
52
50
|
|
51
|
+
private
|
52
|
+
|
53
|
+
# @author Sylvain Daubert
|
54
|
+
# @author adfoster-r7
|
53
55
|
def value_to_der
|
54
56
|
raise ASN1Error, "#{@name}: bit length is not set" if bit_length.nil?
|
55
57
|
|
@@ -70,9 +72,8 @@ module RASN1
|
|
70
72
|
end
|
71
73
|
|
72
74
|
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')
|
75
|
+
value = (@value || '').dup.force_encoding('BINARY')
|
76
|
+
value << "\x00".b while value.length * 8 < @bit_length.to_i
|
76
77
|
return value unless value.length * 8 > @bit_length.to_i
|
77
78
|
|
78
79
|
max_len = @bit_length.to_i / 8 + ((@bit_length.to_i % 8).positive? ? 1 : 0)
|
@@ -85,6 +86,10 @@ module RASN1
|
|
85
86
|
@bit_length = value.length * 8 - unused
|
86
87
|
@value = value
|
87
88
|
end
|
89
|
+
|
90
|
+
def explicit_type
|
91
|
+
self.class.new(name: name, value: @value, bit_length: @bit_length)
|
92
|
+
end
|
88
93
|
end
|
89
94
|
end
|
90
95
|
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/boolean.rb
CHANGED
data/lib/rasn1/types/choice.rb
CHANGED
@@ -88,6 +88,8 @@ module RASN1
|
|
88
88
|
raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
|
89
89
|
end
|
90
90
|
|
91
|
+
# @param [::Integer] level
|
92
|
+
# @return [String]
|
91
93
|
def inspect(level=0)
|
92
94
|
str = common_inspect(level)
|
93
95
|
str << if defined? @chosen
|
@@ -97,10 +99,16 @@ module RASN1
|
|
97
99
|
end
|
98
100
|
end
|
99
101
|
|
102
|
+
# @private Tracer private API
|
103
|
+
# @return [String]
|
104
|
+
def trace
|
105
|
+
msg_type(no_id: true)
|
106
|
+
end
|
107
|
+
|
100
108
|
private
|
101
109
|
|
102
110
|
def check_chosen
|
103
|
-
raise ChoiceError if !defined?(@chosen) || @chosen.nil?
|
111
|
+
raise ChoiceError.new(self) if !defined?(@chosen) || @chosen.nil?
|
104
112
|
end
|
105
113
|
end
|
106
114
|
end
|
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
module RASN1
|
4
4
|
module Types
|
5
|
-
# Mixin to
|
6
|
-
# Should not be used directly but through {
|
5
|
+
# Mixin to add constraints on a RASN1 type.
|
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)
|
@@ -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,50 @@ 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
|
136
|
+
end
|
137
|
+
|
138
|
+
def trace_data
|
139
|
+
return super if explicit?
|
147
140
|
|
148
|
-
|
141
|
+
+' ' << raw_data
|
149
142
|
end
|
150
143
|
end
|
151
144
|
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
|