rasn1 0.10.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 +54 -0
- data/lib/rasn1/model.rb +251 -123
- data/lib/rasn1/types/any.rb +4 -0
- data/lib/rasn1/types/base.rb +47 -27
- data/lib/rasn1/types/bit_string.rb +11 -8
- data/lib/rasn1/types/bmp_string.rb +30 -0
- data/lib/rasn1/types/choice.rb +8 -10
- data/lib/rasn1/types/constrained.rb +55 -0
- 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 +41 -6
- data/lib/rasn1/version.rb +1 -1
- data/lib/rasn1/wrapper.rb +201 -0
- data/lib/rasn1.rb +5 -27
- metadata +10 -34
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]
|
@@ -95,6 +97,13 @@ module RASN1
|
|
95
97
|
obj
|
96
98
|
end
|
97
99
|
|
100
|
+
# Say if a type is constrained.
|
101
|
+
# Always return +false+ for predefined types
|
102
|
+
# @return [Booleran]
|
103
|
+
def self.constrained?
|
104
|
+
false
|
105
|
+
end
|
106
|
+
|
98
107
|
# @param [Hash] options
|
99
108
|
# @option options [Symbol] :class ASN.1 class. Default value is +:universal+.
|
100
109
|
# If +:explicit+ or +:implicit:+ is defined, default value is +:context+.
|
@@ -109,9 +118,14 @@ module RASN1
|
|
109
118
|
# @option options [::String] :name name for this node
|
110
119
|
def initialize(options={})
|
111
120
|
@constructed = nil
|
112
|
-
|
121
|
+
set_value(options.delete(:value))
|
122
|
+
self.options = options
|
123
|
+
specific_initializer
|
113
124
|
end
|
114
125
|
|
126
|
+
# @abstract To help subclass initialize itself. Default implementation do nothing.
|
127
|
+
def specific_initializer; end
|
128
|
+
|
115
129
|
# Used by +#dup+ and +#clone+. Deep copy @value and @default.
|
116
130
|
def initialize_copy(_other)
|
117
131
|
@value = @value.dup
|
@@ -242,11 +256,38 @@ module RASN1
|
|
242
256
|
(other.class == self.class) && (other.to_der == self.to_der)
|
243
257
|
end
|
244
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
|
+
|
245
286
|
private
|
246
287
|
|
247
288
|
def pc_bit
|
248
289
|
if @constructed.nil?
|
249
|
-
self.class
|
290
|
+
self.class.const_get(:ASN1_PC)
|
250
291
|
elsif @constructed # true
|
251
292
|
Constructed::ASN1_PC
|
252
293
|
else # false
|
@@ -285,16 +326,6 @@ module RASN1
|
|
285
326
|
@value = der
|
286
327
|
end
|
287
328
|
|
288
|
-
def set_options(options) # rubocop:disable Naming/AccessorMethodName
|
289
|
-
set_class options[:class]
|
290
|
-
set_optional options[:optional]
|
291
|
-
set_default options[:default]
|
292
|
-
set_tag options
|
293
|
-
set_value options[:value]
|
294
|
-
@name = options[:name]
|
295
|
-
@options = options
|
296
|
-
end
|
297
|
-
|
298
329
|
def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
|
299
330
|
case asn1_class
|
300
331
|
when nil
|
@@ -319,17 +350,15 @@ module RASN1
|
|
319
350
|
# handle undocumented option +:tag_value+, used internally by
|
320
351
|
# {RASN1.parse} to parse non-universal class tags.
|
321
352
|
def set_tag(options) # rubocop:disable Naming/AccessorMethodName
|
353
|
+
@constructed = options[:constructed]
|
322
354
|
if options[:explicit]
|
323
355
|
@tag = :explicit
|
324
356
|
@id_value = options[:explicit]
|
325
|
-
@constructed = options[:constructed]
|
326
357
|
elsif options[:implicit]
|
327
358
|
@tag = :implicit
|
328
359
|
@id_value = options[:implicit]
|
329
|
-
@constructed = options[:constructed]
|
330
360
|
elsif options[:tag_value]
|
331
361
|
@id_value = options[:tag_value]
|
332
|
-
@constructed = options[:constructed]
|
333
362
|
end
|
334
363
|
|
335
364
|
@asn1_class = :context if defined?(@tag) && (@asn1_class == :universal)
|
@@ -346,15 +375,6 @@ module RASN1
|
|
346
375
|
value
|
347
376
|
end
|
348
377
|
|
349
|
-
def value?
|
350
|
-
!@no_value
|
351
|
-
end
|
352
|
-
|
353
|
-
def can_build?
|
354
|
-
(@default.nil? || (value? && (@value != @default))) &&
|
355
|
-
(!optional? || value?)
|
356
|
-
end
|
357
|
-
|
358
378
|
def build
|
359
379
|
if can_build?
|
360
380
|
if explicit?
|
@@ -371,9 +391,9 @@ module RASN1
|
|
371
391
|
end
|
372
392
|
|
373
393
|
def id_value
|
374
|
-
return @id_value if defined?
|
394
|
+
return @id_value if defined?(@id_value) && !@id_value.nil?
|
375
395
|
|
376
|
-
self.class
|
396
|
+
self.class.const_get(:ID)
|
377
397
|
end
|
378
398
|
|
379
399
|
def encode_identifier_octets
|
@@ -455,7 +475,7 @@ module RASN1
|
|
455
475
|
def raise_on_indefinite_length(ber)
|
456
476
|
if primitive?
|
457
477
|
raise ASN1Error, "malformed #{type}: indefinite length " \
|
458
|
-
|
478
|
+
'forbidden for primitive types'
|
459
479
|
elsif ber
|
460
480
|
raise NotImplementedError, 'indefinite length not supported'
|
461
481
|
else
|
@@ -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
@@ -77,15 +77,13 @@ module RASN1
|
|
77
77
|
def parse!(der, ber: false)
|
78
78
|
parsed = false
|
79
79
|
@value.each_with_index do |element, i|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
next
|
88
|
-
end
|
80
|
+
@chosen = i
|
81
|
+
nb_bytes = element.parse!(der, ber: ber)
|
82
|
+
parsed = true
|
83
|
+
return nb_bytes
|
84
|
+
rescue ASN1Error
|
85
|
+
@chosen = nil
|
86
|
+
next
|
89
87
|
end
|
90
88
|
raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless parsed
|
91
89
|
end
|
@@ -102,7 +100,7 @@ module RASN1
|
|
102
100
|
private
|
103
101
|
|
104
102
|
def check_chosen
|
105
|
-
raise ChoiceError if !defined?(@chosen) || @chosen.nil?
|
103
|
+
raise ChoiceError.new(self) if !defined?(@chosen) || @chosen.nil?
|
106
104
|
end
|
107
105
|
end
|
108
106
|
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RASN1
|
4
|
+
module Types
|
5
|
+
# Mixin to had constraints on a RASN1 type.
|
6
|
+
# Should not be used directly but through {Types.define_type}.
|
7
|
+
# @version 0.11.0
|
8
|
+
# @author Sylvain Daubert
|
9
|
+
module Constrained
|
10
|
+
module ClassMethods
|
11
|
+
# Setter for constraint
|
12
|
+
# @param [Proc,nil] constraint
|
13
|
+
# @return [Proc,nil]
|
14
|
+
def constraint=(constraint)
|
15
|
+
@constraint = constraint
|
16
|
+
end
|
17
|
+
|
18
|
+
# Check if a constraint is really defined
|
19
|
+
# @return [Boolean]
|
20
|
+
def constrained?
|
21
|
+
@constraint.is_a?(Proc)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Check constraint, if defined
|
25
|
+
# @param [Object] value the value of the type to check
|
26
|
+
# @raise [ConstraintError] constraint is not verified
|
27
|
+
def check_constraint(value)
|
28
|
+
return unless constrained?
|
29
|
+
raise ConstraintError.new(self) unless @constraint.call(value)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
attr_reader :constraint
|
35
|
+
|
36
|
+
def included(base)
|
37
|
+
base.extend ClassMethods
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Redefined +#value=+ to check constraint before assigning +val+
|
42
|
+
# @see Types::Base#value=
|
43
|
+
# @raise [ConstraintError] constraint is not verified
|
44
|
+
def value=(val)
|
45
|
+
self.class.check_constraint(val)
|
46
|
+
super
|
47
|
+
end
|
48
|
+
|
49
|
+
def der_to_value(der, ber: false)
|
50
|
+
super
|
51
|
+
self.class.check_constraint(@value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -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,12 +74,39 @@ 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
|
75
82
|
end
|
83
|
+
|
84
|
+
# Define a new ASN.1 type from a base one.
|
85
|
+
# This new type may have a constraint defines on it.
|
86
|
+
# @param [Symbol,String] name New type name. Must start with a capital letter.
|
87
|
+
# @param [Types::Base] from class from which inherits
|
88
|
+
# @param [Module] in_module module in which creates new type (default to {RASN1::Types})
|
89
|
+
# @return [Class] newly created class
|
90
|
+
# @since 0.11.0
|
91
|
+
# @since 0.12.0 in_module parameter
|
92
|
+
def self.define_type(name, from:, in_module: self, &block)
|
93
|
+
constraint = block.nil? ? nil : block.to_proc
|
94
|
+
|
95
|
+
new_klass = Class.new(from) do
|
96
|
+
include Constrained
|
97
|
+
end
|
98
|
+
new_klass.constraint = constraint
|
99
|
+
|
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)
|
103
|
+
|
104
|
+
# Empty type caches
|
105
|
+
@primitives = []
|
106
|
+
@constructed = []
|
107
|
+
|
108
|
+
new_klass
|
109
|
+
end
|
76
110
|
end
|
77
111
|
end
|
78
112
|
|
@@ -85,6 +119,7 @@ require_relative 'types/octet_string'
|
|
85
119
|
require_relative 'types/null'
|
86
120
|
require_relative 'types/object_id'
|
87
121
|
require_relative 'types/enumerated'
|
122
|
+
require_relative 'types/bmp_string'
|
88
123
|
require_relative 'types/utf8_string'
|
89
124
|
require_relative 'types/numeric_string'
|
90
125
|
require_relative 'types/printable_string'
|