rasn1 0.14.0 → 0.15.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 +13 -6
- data/lib/rasn1/model.rb +145 -138
- data/lib/rasn1/tracer.rb +4 -4
- data/lib/rasn1/types/any.rb +5 -3
- data/lib/rasn1/types/base.rb +43 -30
- data/lib/rasn1/types/bit_string.rb +12 -7
- data/lib/rasn1/types/bmp_string.rb +3 -11
- data/lib/rasn1/types/boolean.rb +15 -8
- data/lib/rasn1/types/choice.rb +43 -3
- data/lib/rasn1/types/constrained.rb +5 -2
- data/lib/rasn1/types/generalized_time.rb +32 -24
- data/lib/rasn1/types/ia5string.rb +3 -11
- data/lib/rasn1/types/integer.rb +13 -9
- data/lib/rasn1/types/null.rb +11 -6
- data/lib/rasn1/types/numeric_string.rb +13 -6
- data/lib/rasn1/types/object_id.rb +33 -21
- data/lib/rasn1/types/octet_string.rb +26 -0
- data/lib/rasn1/types/printable_string.rb +13 -6
- data/lib/rasn1/types/sequence.rb +16 -12
- data/lib/rasn1/types/sequence_of.rb +15 -11
- data/lib/rasn1/types/universal_string.rb +3 -11
- data/lib/rasn1/types/utc_time.rb +17 -7
- data/lib/rasn1/types/utf8_string.rb +3 -11
- data/lib/rasn1/types/visible_string.rb +14 -2
- data/lib/rasn1/types.rb +6 -5
- data/lib/rasn1/version.rb +2 -1
- data/lib/rasn1/wrapper.rb +72 -18
- data/lib/rasn1.rb +4 -1
- metadata +2 -2
data/lib/rasn1/types/base.rb
CHANGED
@@ -6,8 +6,8 @@ module RASN1
|
|
6
6
|
#
|
7
7
|
# Subclasses SHOULD define:
|
8
8
|
# * an ID constant defining ASN.1 BER/DER identification number,
|
9
|
+
# * a method {#der_to_value} converting DER into {#value}.
|
9
10
|
# * a private method {#value_to_der} converting its {#value} to DER,
|
10
|
-
# * a private method {#der_to_value} converting DER into {#value}.
|
11
11
|
#
|
12
12
|
# ==Define an optional value
|
13
13
|
# An optional value may be defined using +:optional+ key from {#initialize}:
|
@@ -178,14 +178,14 @@ module RASN1
|
|
178
178
|
# @return [::Boolean,nil] return +nil+ if not tagged, return +true+
|
179
179
|
# if explicit, else +false+
|
180
180
|
def explicit?
|
181
|
-
defined?(@tag) ? @tag == :explicit : nil
|
181
|
+
defined?(@tag) ? @tag == :explicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
182
182
|
end
|
183
183
|
|
184
184
|
# Say if a tagged type is implicit
|
185
185
|
# @return [::Boolean,nil] return +nil+ if not tagged, return +true+
|
186
186
|
# if implicit, else +false+
|
187
187
|
def implicit?
|
188
|
-
defined?(@tag) ? @tag == :implicit : nil
|
188
|
+
defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
189
189
|
end
|
190
190
|
|
191
191
|
# @abstract This method SHOULD be partly implemented by subclasses, which
|
@@ -225,7 +225,7 @@ module RASN1
|
|
225
225
|
# @return [Integer] total number of parsed bytes
|
226
226
|
# @raise [ASN1Error] error on parsing
|
227
227
|
def parse!(der, ber: false)
|
228
|
-
total_length, data = do_parse(der, ber)
|
228
|
+
total_length, data = do_parse(der, ber: ber)
|
229
229
|
return 0 if total_length.zero?
|
230
230
|
|
231
231
|
if explicit?
|
@@ -300,6 +300,41 @@ module RASN1
|
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
303
|
+
# @private Parse tage and length from binary string. Return data length and binary data.
|
304
|
+
# @param [String] der
|
305
|
+
# @param [Boolean] ber
|
306
|
+
# @return [Array(::Integer, String)]
|
307
|
+
# @since 0.15.0 was private before
|
308
|
+
def do_parse(der, ber: false)
|
309
|
+
return [0, ''] unless check_id(der)
|
310
|
+
|
311
|
+
id_size = Types.decode_identifier_octets(der).last
|
312
|
+
total_length, data = get_data(der[id_size..], ber)
|
313
|
+
total_length += id_size
|
314
|
+
@no_value = false
|
315
|
+
|
316
|
+
[total_length, data]
|
317
|
+
end
|
318
|
+
|
319
|
+
# @private Delegate to #explicit type to generate sub-value
|
320
|
+
# @param [String] data
|
321
|
+
# @return [void]
|
322
|
+
# @since 0.15.0 was private before
|
323
|
+
def do_parse_explicit(data)
|
324
|
+
type = explicit_type
|
325
|
+
type.parse!(data)
|
326
|
+
@value = type.value
|
327
|
+
end
|
328
|
+
|
329
|
+
# Make value from DER/BER string
|
330
|
+
# @param [String] der
|
331
|
+
# @param [::Boolean] ber
|
332
|
+
# @return [void]
|
333
|
+
# @since 0.15.0 was private before
|
334
|
+
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
335
|
+
@value = der
|
336
|
+
end
|
337
|
+
|
303
338
|
private
|
304
339
|
|
305
340
|
def trace_real
|
@@ -349,7 +384,7 @@ module RASN1
|
|
349
384
|
end
|
350
385
|
|
351
386
|
def msg_type(no_id: false)
|
352
|
-
msg = name.nil? ? +'' :
|
387
|
+
msg = name.nil? ? +'' : "#{name} "
|
353
388
|
msg << "[ #{asn1_class_to_s}#{id} ] " unless no_id
|
354
389
|
msg << if explicit?
|
355
390
|
+'EXPLICIT '
|
@@ -391,24 +426,6 @@ module RASN1
|
|
391
426
|
end
|
392
427
|
end
|
393
428
|
|
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
|
-
|
412
429
|
def value_to_der
|
413
430
|
case @value
|
414
431
|
when Base
|
@@ -418,10 +435,6 @@ module RASN1
|
|
418
435
|
end
|
419
436
|
end
|
420
437
|
|
421
|
-
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
422
|
-
@value = der
|
423
|
-
end
|
424
|
-
|
425
438
|
def set_class(asn1_class) # rubocop:disable Naming/AccessorMethodName
|
426
439
|
case asn1_class
|
427
440
|
when nil
|
@@ -595,13 +608,13 @@ module RASN1
|
|
595
608
|
end
|
596
609
|
|
597
610
|
def raise_id_error(der)
|
598
|
-
msg = name.nil? ? +'' :
|
611
|
+
msg = name.nil? ? +'' : "#{name}: "
|
599
612
|
msg << "Expected #{self2name} but get #{der2name(der)}"
|
600
613
|
raise ASN1Error, msg
|
601
614
|
end
|
602
615
|
|
603
616
|
def self2name
|
604
|
-
name =
|
617
|
+
name = "#{asn1_class.to_s.upcase} #{constructed? ? 'CONSTRUCTED' : 'PRIMITIVE'}"
|
605
618
|
if implicit? || explicit?
|
606
619
|
name << ' 0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
|
607
620
|
else
|
@@ -613,7 +626,7 @@ module RASN1
|
|
613
626
|
return 'no ID' if der.nil? || der.empty?
|
614
627
|
|
615
628
|
asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
|
616
|
-
name =
|
629
|
+
name = "#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
|
617
630
|
type = find_type(id)
|
618
631
|
name << " #{type.nil? ? '0x%X (0x%s)' % [id, bin2hex(der[0...id_size])] : type.encoded_type}"
|
619
632
|
end
|
@@ -48,6 +48,18 @@ module RASN1
|
|
48
48
|
super || (!@default.nil? && (@bit_length != @default_bit_length))
|
49
49
|
end
|
50
50
|
|
51
|
+
# Make value from DER/BER string. Also set {#bit_length}.
|
52
|
+
# @param [String] der
|
53
|
+
# @param [::Boolean] ber
|
54
|
+
# @return [void]
|
55
|
+
# @see Types::Base#der_to_value
|
56
|
+
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
57
|
+
unused = der.unpack1('C').to_i
|
58
|
+
value = der[1..].to_s
|
59
|
+
@bit_length = value.length * 8 - unused
|
60
|
+
@value = value
|
61
|
+
end
|
62
|
+
|
51
63
|
private
|
52
64
|
|
53
65
|
# @author Sylvain Daubert
|
@@ -80,13 +92,6 @@ module RASN1
|
|
80
92
|
value[0, max_len].to_s
|
81
93
|
end
|
82
94
|
|
83
|
-
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
84
|
-
unused = der.unpack1('C').to_i
|
85
|
-
value = der[1..-1].to_s
|
86
|
-
@bit_length = value.length * 8 - unused
|
87
|
-
@value = value
|
88
|
-
end
|
89
|
-
|
90
95
|
def explicit_type
|
91
96
|
self.class.new(name: name, value: @value, bit_length: @bit_length)
|
92
97
|
end
|
@@ -8,23 +8,15 @@ module RASN1
|
|
8
8
|
class BmpString < OctetString
|
9
9
|
# BmpString id value
|
10
10
|
ID = 30
|
11
|
+
# UniversalString encoding
|
12
|
+
# @since 0.15.0
|
13
|
+
ENCODING = Encoding::UTF_16BE
|
11
14
|
|
12
15
|
# Get ASN.1 type
|
13
16
|
# @return [String]
|
14
17
|
def self.type
|
15
18
|
'BmpString'
|
16
19
|
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
20
|
end
|
29
21
|
end
|
30
22
|
end
|
data/lib/rasn1/types/boolean.rb
CHANGED
@@ -8,9 +8,9 @@ module RASN1
|
|
8
8
|
# Boolean id value
|
9
9
|
ID = 0x01
|
10
10
|
|
11
|
-
#
|
11
|
+
# DER true value
|
12
12
|
DER_TRUE = 0xff
|
13
|
-
#
|
13
|
+
# DER false value
|
14
14
|
DER_FALSE = 0
|
15
15
|
|
16
16
|
# @return [false]
|
@@ -18,12 +18,13 @@ module RASN1
|
|
18
18
|
false
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
# Make boolean value from DER/BER string.
|
22
|
+
# @param [String] der
|
23
|
+
# @param [::Boolean] ber
|
24
|
+
# @return [void]
|
25
|
+
# @see Types::Base#der_to_value
|
26
|
+
# @raise [ASN1Error] +der+ is not 1-byte long
|
27
|
+
# @raise [ASN1Error] +der+ is not {DER_TRUE} nor {DER_FALSE} and +ber+ is +false+
|
27
28
|
def der_to_value(der, ber: false)
|
28
29
|
raise ASN1Error, "tag #{@name}: BOOLEAN should have a length of 1" unless der.size == 1
|
29
30
|
|
@@ -40,6 +41,12 @@ module RASN1
|
|
40
41
|
end
|
41
42
|
end
|
42
43
|
|
44
|
+
private
|
45
|
+
|
46
|
+
def value_to_der
|
47
|
+
[@value ? DER_TRUE : DER_FALSE].pack('C')
|
48
|
+
end
|
49
|
+
|
43
50
|
def trace_data
|
44
51
|
return super if explicit?
|
45
52
|
|
data/lib/rasn1/types/choice.rb
CHANGED
@@ -57,6 +57,8 @@ module RASN1
|
|
57
57
|
@value[@chosen].value
|
58
58
|
when Model
|
59
59
|
@value[@chosen]
|
60
|
+
when Wrapper
|
61
|
+
@value[@chosen].element
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
@@ -75,10 +77,25 @@ module RASN1
|
|
75
77
|
# @return [Integer] total number of parsed bytes
|
76
78
|
# @raise [ASN1Error] error on parsing
|
77
79
|
def parse!(der, ber: false)
|
80
|
+
len, data = do_parse(der, ber: ber)
|
81
|
+
return 0 if len.zero?
|
82
|
+
|
83
|
+
element = @value[@chosen]
|
84
|
+
if element.explicit?
|
85
|
+
element.do_parse_explicit(data)
|
86
|
+
else
|
87
|
+
element.der_to_value(data, ber: ber)
|
88
|
+
end
|
89
|
+
len
|
90
|
+
end
|
91
|
+
|
92
|
+
# @private
|
93
|
+
# @see Types::Base#do_parse
|
94
|
+
# @since 0.15.0 Specific +#do_parse+ to handle recursivity
|
95
|
+
def do_parse(der, ber: false)
|
78
96
|
@value.each_with_index do |element, i|
|
79
97
|
@chosen = i
|
80
|
-
|
81
|
-
return nb_bytes
|
98
|
+
return element.do_parse(der, ber: ber)
|
82
99
|
rescue ASN1Error
|
83
100
|
@chosen = nil
|
84
101
|
next
|
@@ -88,7 +105,23 @@ module RASN1
|
|
88
105
|
@value = void_value
|
89
106
|
raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless optional?
|
90
107
|
|
91
|
-
0
|
108
|
+
[0, ''.b]
|
109
|
+
end
|
110
|
+
|
111
|
+
# Make choice value from DER/BER string.
|
112
|
+
# @param [String] der
|
113
|
+
# @param [::Boolean] ber
|
114
|
+
# @return [void]
|
115
|
+
# @since 0.15.0 Specific +#der_to_value+ to handle recursivity
|
116
|
+
def der_to_value(der, ber: false)
|
117
|
+
@value.each_with_index do |element, i|
|
118
|
+
@chosen = i
|
119
|
+
element.parse!(der, ber: ber)
|
120
|
+
break
|
121
|
+
rescue ASN1Error
|
122
|
+
@chosen = nil
|
123
|
+
next
|
124
|
+
end
|
92
125
|
end
|
93
126
|
|
94
127
|
# @param [::Integer] level
|
@@ -108,6 +141,13 @@ module RASN1
|
|
108
141
|
msg_type(no_id: true)
|
109
142
|
end
|
110
143
|
|
144
|
+
# Return empty array
|
145
|
+
# @return [Array()]
|
146
|
+
# @since 0.15.0
|
147
|
+
def void_value
|
148
|
+
[]
|
149
|
+
end
|
150
|
+
|
111
151
|
private
|
112
152
|
|
113
153
|
def check_chosen
|
@@ -37,8 +37,11 @@ module RASN1
|
|
37
37
|
super
|
38
38
|
end
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
# Make value from +der+ string and check constraints
|
41
|
+
# @param [String] der
|
42
|
+
# @param [::Boolean] ber
|
43
|
+
# @return [void]
|
44
|
+
# @raise [ConstraintError] constraint is not verified
|
42
45
|
def der_to_value(der, ber: false)
|
43
46
|
super
|
44
47
|
self.class.check_constraint(@value)
|
@@ -44,18 +44,10 @@ module RASN1
|
|
44
44
|
Time.now
|
45
45
|
end
|
46
46
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
if utc_value.nsec.positive?
|
52
|
-
der = utc_value.strftime('%Y%m%d%H%M%S.%9NZ')
|
53
|
-
der.sub(/0+Z/, 'Z')
|
54
|
-
else
|
55
|
-
utc_value.strftime('%Y%m%d%H%M%SZ')
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
47
|
+
# Make time value from +der+ string
|
48
|
+
# @param [String] der
|
49
|
+
# @param [::Boolean] ber
|
50
|
+
# @return [void]
|
59
51
|
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
60
52
|
date_hour, fraction = der.split('.')
|
61
53
|
date_hour = date_hour.to_s
|
@@ -70,19 +62,20 @@ module RASN1
|
|
70
62
|
end
|
71
63
|
end
|
72
64
|
|
65
|
+
private
|
66
|
+
|
67
|
+
def value_to_der
|
68
|
+
utc_value = @value.getutc
|
69
|
+
if utc_value.nsec.positive?
|
70
|
+
der = utc_value.strftime('%Y%m%d%H%M%S.%9NZ')
|
71
|
+
der.sub(/0+Z/, 'Z')
|
72
|
+
else
|
73
|
+
utc_value.strftime('%Y%m%d%H%M%SZ')
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
73
77
|
def value_when_fraction_empty(date_hour)
|
74
|
-
|
75
|
-
# From 3.1: "Z" and "-0100" are supported
|
76
|
-
# Below 3.1: should be "-01:00" or "+00:00"
|
77
|
-
tz = if date_hour[-1] == 'Z'
|
78
|
-
date_hour.slice!(-1, 1)
|
79
|
-
'+00:00' # Ruby 3.0: to remove after end-of support of ruby 3.0
|
80
|
-
elsif date_hour.match?(/[+-]\d+$/)
|
81
|
-
# Ruby 3.0
|
82
|
-
# date_hour.slice!(-5, 5)
|
83
|
-
zone = date_hour.slice!(-5, 5).to_s
|
84
|
-
"#{zone[0, 3]}:#{zone[3, 2]}"
|
85
|
-
end
|
78
|
+
tz = compute_tz(date_hour)
|
86
79
|
year = date_hour.slice!(0, 4).to_i
|
87
80
|
month = date_hour.slice!(0, 2).to_i
|
88
81
|
day = date_hour.slice!(0, 2).to_i
|
@@ -92,6 +85,21 @@ module RASN1
|
|
92
85
|
@value = Time.new(year, month, day, hour, minute, second, tz)
|
93
86
|
end
|
94
87
|
|
88
|
+
# Ruby 3.0: special handle for timezone
|
89
|
+
# From 3.1: "Z" and "-0100" are supported
|
90
|
+
# Below 3.1: should be "-01:00" or "+00:00"
|
91
|
+
def compute_tz(date_hour)
|
92
|
+
if date_hour.end_with?('Z')
|
93
|
+
date_hour.slice!(-1, 1)
|
94
|
+
'+00:00' # Ruby 3.0: to remove after end-of support of ruby 3.0
|
95
|
+
elsif date_hour.match?(/[+-]\d+$/)
|
96
|
+
# Ruby 3.0
|
97
|
+
# date_hour.slice!(-5, 5)
|
98
|
+
zone = date_hour.slice!(-5, 5).to_s
|
99
|
+
"#{zone[0, 3]}:#{zone[3, 2]}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
95
103
|
def value_when_fraction_ends_with_z(date_hour, fraction)
|
96
104
|
fraction = fraction[0...-1]
|
97
105
|
date_hour << 'Z'
|
@@ -7,23 +7,15 @@ module RASN1
|
|
7
7
|
class IA5String < OctetString
|
8
8
|
# IA5String id value
|
9
9
|
ID = 22
|
10
|
+
# UniversalString encoding
|
11
|
+
# @since 0.15.0
|
12
|
+
ENCODING = Encoding::US_ASCII
|
10
13
|
|
11
14
|
# Get ASN.1 type
|
12
15
|
# @return [String]
|
13
16
|
def self.type
|
14
17
|
'IA5String'
|
15
18
|
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def value_to_der
|
20
|
-
@value.to_s.dup.force_encoding('US-ASCII').b
|
21
|
-
end
|
22
|
-
|
23
|
-
def der_to_value(der, ber: false)
|
24
|
-
super
|
25
|
-
@value.to_s.dup.force_encoding('US-ASCII')
|
26
|
-
end
|
27
19
|
end
|
28
20
|
end
|
29
21
|
end
|
data/lib/rasn1/types/integer.rb
CHANGED
@@ -54,6 +54,19 @@ module RASN1
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
# Make integer value from +der+ string.
|
58
|
+
# @param [String] der
|
59
|
+
# @param [::Boolean] ber
|
60
|
+
# @return [void]
|
61
|
+
def der_to_value(der, ber: false)
|
62
|
+
int_value = der_to_int_value(der, ber: ber)
|
63
|
+
@value = if @enum.empty?
|
64
|
+
int_value
|
65
|
+
else
|
66
|
+
int_to_enum(int_value)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
57
70
|
private
|
58
71
|
|
59
72
|
def initialize_enum(enum)
|
@@ -136,15 +149,6 @@ module RASN1
|
|
136
149
|
@enum.key(int) || int
|
137
150
|
end
|
138
151
|
|
139
|
-
def der_to_value(der, ber: false)
|
140
|
-
int_value = der_to_int_value(der, ber: ber)
|
141
|
-
@value = if @enum.empty?
|
142
|
-
int_value
|
143
|
-
else
|
144
|
-
int_to_enum(int_value)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
152
|
def explicit_type
|
149
153
|
self.class.new(name: name, enum: enum)
|
150
154
|
end
|
data/lib/rasn1/types/null.rb
CHANGED
@@ -21,18 +21,23 @@ module RASN1
|
|
21
21
|
!optional?
|
22
22
|
end
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
# Check +der+ string
|
25
|
+
# @param [String] der
|
26
|
+
# @param [::Boolean] ber
|
27
|
+
# @return [void]
|
28
|
+
# @raise [ASN1Error] +der+ is not empty
|
30
29
|
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
31
30
|
raise ASN1Error, 'NULL should not have content!' if der.length.positive?
|
32
31
|
|
33
32
|
@no_value = true
|
34
33
|
@value = void_value
|
35
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def value_to_der
|
39
|
+
''
|
40
|
+
end
|
36
41
|
end
|
37
42
|
end
|
38
43
|
end
|
@@ -7,6 +7,8 @@ module RASN1
|
|
7
7
|
class NumericString < OctetString
|
8
8
|
# NumericString id value
|
9
9
|
ID = 18
|
10
|
+
# Invalid characters in NumericString
|
11
|
+
INVALID_CHARS = /([^0-9 ])/
|
10
12
|
|
11
13
|
# Get ASN.1 type
|
12
14
|
# @return [String]
|
@@ -14,6 +16,16 @@ module RASN1
|
|
14
16
|
'NumericString'
|
15
17
|
end
|
16
18
|
|
19
|
+
# Make string value from +der+ string
|
20
|
+
# @param [String] der
|
21
|
+
# @param [::Boolean] ber
|
22
|
+
# @return [void]
|
23
|
+
# @raise [ASN1Error] invalid characters detected
|
24
|
+
def der_to_value(der, ber: false)
|
25
|
+
super
|
26
|
+
check_characters
|
27
|
+
end
|
28
|
+
|
17
29
|
private
|
18
30
|
|
19
31
|
def value_to_der
|
@@ -21,13 +33,8 @@ module RASN1
|
|
21
33
|
@value.to_s.b
|
22
34
|
end
|
23
35
|
|
24
|
-
def der_to_value(der, ber: false)
|
25
|
-
super
|
26
|
-
check_characters
|
27
|
-
end
|
28
|
-
|
29
36
|
def check_characters
|
30
|
-
raise ASN1Error, "NUMERIC STRING #{@name}: invalid character: '#{$1}'" if @value.to_s =~
|
37
|
+
raise ASN1Error, "NUMERIC STRING #{@name}: invalid character: '#{$1}'" if @value.to_s =~ INVALID_CHARS
|
31
38
|
end
|
32
39
|
end
|
33
40
|
end
|
@@ -4,30 +4,15 @@ module RASN1
|
|
4
4
|
module Types
|
5
5
|
# ASN.1 Object ID
|
6
6
|
# @author Sylvain Daubert
|
7
|
+
# @author LemonTree55
|
7
8
|
class ObjectId < Primitive
|
8
9
|
# ObjectId id value
|
9
10
|
ID = 6
|
10
11
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
raise ASN1Error, "OBJECT ID #{@name}: first subidentifier should be less than 3" if ids[0] > 2
|
17
|
-
raise ASN1Error, "OBJECT ID #{@name}: second subidentifier should be less than 40" if (ids[0] < 2) && (ids[1] > 39)
|
18
|
-
|
19
|
-
ids[0, 2] = ids[0] * 40 + ids[1]
|
20
|
-
octets = []
|
21
|
-
ids.each do |v|
|
22
|
-
if v < 128
|
23
|
-
octets << v
|
24
|
-
else
|
25
|
-
octets.concat(unsigned_to_chained_octets(v))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
octets.pack('C*')
|
29
|
-
end
|
30
|
-
|
12
|
+
# Make object id value from +der+ string
|
13
|
+
# @param [String] der
|
14
|
+
# @param [::Boolean] ber
|
15
|
+
# @return [void]
|
31
16
|
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
32
17
|
bytes = der.unpack('C*')
|
33
18
|
|
@@ -35,7 +20,7 @@ module RASN1
|
|
35
20
|
current_id = 0
|
36
21
|
bytes.each do |byte|
|
37
22
|
current_id = (current_id << 7) | (byte & 0x7f)
|
38
|
-
if (
|
23
|
+
if byte.nobits?(0x80)
|
39
24
|
ids << current_id
|
40
25
|
current_id = 0
|
41
26
|
end
|
@@ -47,6 +32,33 @@ module RASN1
|
|
47
32
|
|
48
33
|
@value = ids.join('.')
|
49
34
|
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# @raise [ASN1Error]
|
39
|
+
def value_to_der
|
40
|
+
ids = @value.to_s.split('.').map(&:to_i)
|
41
|
+
|
42
|
+
check_first_ids(ids)
|
43
|
+
|
44
|
+
ids[0, 2] = ids[0] * 40 + ids[1]
|
45
|
+
octets = []
|
46
|
+
ids.each do |v|
|
47
|
+
if v < 128
|
48
|
+
octets << v
|
49
|
+
else
|
50
|
+
octets.concat(unsigned_to_chained_octets(v))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
octets.pack('C*')
|
54
|
+
end
|
55
|
+
|
56
|
+
# @param [Array[::Integer]] ids
|
57
|
+
# @raise [ASN1Error]
|
58
|
+
def check_first_ids(ids)
|
59
|
+
raise ASN1Error, "OBJECT ID #{@name}: first subidentifier should be less than 3" if ids[0] > 2
|
60
|
+
raise ASN1Error, "OBJECT ID #{@name}: second subidentifier should be less than 40" if (ids[0] < 2) && (ids[1] > 39)
|
61
|
+
end
|
50
62
|
end
|
51
63
|
end
|
52
64
|
end
|
@@ -21,6 +21,32 @@ module RASN1
|
|
21
21
|
str = common_inspect(level)
|
22
22
|
str << " #{value.inspect}"
|
23
23
|
end
|
24
|
+
|
25
|
+
# Make string value from +der+ string. Force encoding to UTF-8
|
26
|
+
# @param [String] der
|
27
|
+
# @param [::Boolean] ber
|
28
|
+
# @return [void]
|
29
|
+
# @since 0.15.0 Handle ENCODING constant, if defined by subclass
|
30
|
+
def der_to_value(der, ber: false)
|
31
|
+
klass = self.class
|
32
|
+
if klass.const_defined?(:ENCODING)
|
33
|
+
@value = der.to_s.dup.force_encoding(klass.const_get(:ENCODING))
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @since 0.15.0 Handle ENCODING constant, if defined by subclass
|
42
|
+
def value_to_der
|
43
|
+
klass = self.class
|
44
|
+
if klass.const_defined?(:ENCODING)
|
45
|
+
@value.to_s.encode(klass.const_get(:ENCODING)).b
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
24
50
|
end
|
25
51
|
end
|
26
52
|
end
|