rasn1 0.14.0 → 0.15.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/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
|