rasn2 1.0 → 1.0.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/rasn2/helpers/colorize.rb +66 -23
- data/lib/rasn2/model.rb +6 -2
- data/lib/rasn2/schema_parser.rb +1 -1
- data/lib/rasn2/tracer.rb +3 -2
- data/lib/rasn2/types/any.rb +17 -33
- data/lib/rasn2/types/base.rb +111 -73
- data/lib/rasn2/types/bit_string.rb +2 -5
- data/lib/rasn2/types/boolean.rb +1 -1
- data/lib/rasn2/types/choice.rb +3 -3
- data/lib/rasn2/types/constructed.rb +0 -23
- data/lib/rasn2/types/ia5_string.rb +4 -0
- data/lib/rasn2/types/integer.rb +2 -1
- data/lib/rasn2/types/null.rb +5 -4
- data/lib/rasn2/types/octet_string.rb +2 -5
- data/lib/rasn2/types/sequence_of.rb +10 -15
- data/lib/rasn2/types/tag.rb +12 -27
- data/lib/rasn2/types.rb +4 -2
- data/lib/rasn2/version.rb +1 -1
- data/lib/rasn2/wrapper.rb +0 -7
- data/lib/rasn2.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0bc40f5198815e03f0bfa923321ac614e68d4587f31b3cb18d999f40e7b49004
|
|
4
|
+
data.tar.gz: '09ec989aa9f0674cf2f4ed956518bd717a85de33357b7c09cd2fe6a2d2caa9b8'
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7739ea22723954d0ee78787697e8dde59bb0b41f6fb3c23c8e34f0cab1bd23720b35e05abffb8ac389141115f598d61cf28ce32ccd7d078169cf16bf0043616c
|
|
7
|
+
data.tar.gz: e5ef77bb7b20f1bb6819c4935e04a302c58930bc183371b132522c0b245da9e306de8fbcb29653c535c8d6646f991ff990726466937f487a5c7ea0470034a342
|
|
@@ -1,44 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module RASN2
|
|
2
4
|
module Helpers
|
|
3
5
|
module Colorize
|
|
6
|
+
def colorizer
|
|
7
|
+
@colorizer&.first || Rainbow.new
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def colorizer=(colorizer)
|
|
11
|
+
@colorizer ||= []
|
|
12
|
+
@colorizer << colorizer
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def begin_colorizer(color)
|
|
16
|
+
@colorizer ||= []
|
|
17
|
+
@colorizer << Rainbow.new
|
|
18
|
+
@colorizer.last.enabled = color
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def colorize(msg)
|
|
22
|
+
colorizer.wrap(msg)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def end_colorizer
|
|
26
|
+
@colorizer&.pop
|
|
27
|
+
end
|
|
28
|
+
|
|
4
29
|
def parens_hex(input, padding=2)
|
|
5
|
-
|
|
6
|
-
|
|
30
|
+
value = if input.is_a?(Integer)
|
|
31
|
+
if input >= 0
|
|
32
|
+
value = "%0#{padding}X" % input
|
|
33
|
+
value = value.rjust((value.length + 1) / 2 * 2, '0')
|
|
34
|
+
"0x#{value}"
|
|
35
|
+
else
|
|
36
|
+
'0xFF'
|
|
37
|
+
end
|
|
38
|
+
else
|
|
39
|
+
input
|
|
40
|
+
end
|
|
41
|
+
"#{colorize('(').webgray}#{colorize(value).blue}#{colorize(')').webgray}"
|
|
7
42
|
end
|
|
8
43
|
|
|
9
44
|
def int_with_hex(input, raw_value=nil)
|
|
10
|
-
if raw_value
|
|
11
|
-
"#{colorize(input).blue} #{parens_hex(raw_value)}"
|
|
12
|
-
else
|
|
45
|
+
if raw_value.nil?
|
|
13
46
|
"#{colorize(input).blue} #{parens_hex(input)}"
|
|
47
|
+
else
|
|
48
|
+
"#{colorize(input).blue} #{parens_hex(raw_value)}"
|
|
14
49
|
end
|
|
15
|
-
|
|
16
50
|
end
|
|
17
51
|
|
|
18
52
|
def brace_surround(input)
|
|
19
53
|
"#{colorize('[').webgray} #{input} #{colorize(']').webgray}"
|
|
20
54
|
end
|
|
21
55
|
|
|
22
|
-
def colorize_class(name, tag_id=nil)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
end
|
|
56
|
+
def colorize_class(name, tag_id=nil, *attributes)
|
|
57
|
+
parts = [colorize(name).bold.yellow]
|
|
58
|
+
parts += attributes.map { |attr| colorize_attribute(attr) } if attributes.any?
|
|
59
|
+
parts << parens_hex(tag_id) if tag_id
|
|
60
|
+
parts.join(' ')
|
|
28
61
|
end
|
|
29
62
|
|
|
30
63
|
def colorize_id(id, tag_class=nil)
|
|
31
64
|
id = id.strip if id.is_a? String
|
|
32
65
|
tag_class = tag_class.strip if tag_class.is_a? String
|
|
66
|
+
tag_class = '' if tag_class == 'UNIVERSAL'
|
|
67
|
+
|
|
68
|
+
return '' if id.to_s == '' && tag_class.to_s == ''
|
|
69
|
+
|
|
33
70
|
if tag_class && tag_class != ''
|
|
34
71
|
brace_surround("#{colorize_class(tag_class)} #{colorize(id).bold.green}")
|
|
35
72
|
else
|
|
36
73
|
brace_surround(colorize(id).bold.green)
|
|
37
74
|
end
|
|
38
|
-
|
|
39
75
|
end
|
|
40
76
|
|
|
41
|
-
def length_specifier(data_length, encoded_length
|
|
77
|
+
def length_specifier(data_length, encoded_length=nil)
|
|
42
78
|
encoded_length = data_length if encoded_length.nil?
|
|
43
79
|
"#{colorize('len:').gray} #{colorize(data_length).blue} #{parens_hex(encoded_length)}"
|
|
44
80
|
end
|
|
@@ -52,25 +88,32 @@ module RASN2
|
|
|
52
88
|
end
|
|
53
89
|
|
|
54
90
|
def colorize_default(value)
|
|
55
|
-
colorize(value).green.bold
|
|
91
|
+
" #{colorize_attribute('DEFAULT VALUE')} #{colorize(value).green.bold}"
|
|
56
92
|
end
|
|
57
93
|
|
|
58
|
-
def colorize_nil(name
|
|
94
|
+
def colorize_nil(name='NONE')
|
|
59
95
|
colorize(name).red.bold
|
|
60
96
|
end
|
|
61
97
|
|
|
62
98
|
def colorize_enum(name, raw_value)
|
|
63
|
-
raw_value = raw_value.unpack1('H*') if raw_value.is_a?(String)
|
|
99
|
+
raw_value = "0x#{raw_value.unpack1('H*').upcase}" if raw_value.is_a?(String)
|
|
64
100
|
"#{colorize_class(name)} #{parens_hex(raw_value)}"
|
|
65
101
|
end
|
|
66
102
|
|
|
103
|
+
def colorize_value(value)
|
|
104
|
+
colorize(value).blue
|
|
105
|
+
end
|
|
106
|
+
|
|
67
107
|
def colorize_bool(value, raw_value)
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
108
|
+
hex_raw_value = raw_value.is_a?(String) ? "0x#{raw_value.unpack1('H*').upcase}" : raw_value
|
|
109
|
+
value = if value
|
|
110
|
+
colorize('TRUE').bold.green
|
|
111
|
+
else
|
|
112
|
+
colorize('FALSE').bold.red
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
"#{value} #{parens_hex(hex_raw_value)}"
|
|
73
116
|
end
|
|
74
117
|
end
|
|
75
118
|
end
|
|
76
|
-
end
|
|
119
|
+
end
|
data/lib/rasn2/model.rb
CHANGED
|
@@ -129,6 +129,8 @@ module RASN2
|
|
|
129
129
|
# @private Sequence types
|
|
130
130
|
SEQUENCE_TYPES = [Types::Sequence, Types::SequenceOf, Types::Set, Types::SetOf].freeze
|
|
131
131
|
|
|
132
|
+
include Helpers::Colorize
|
|
133
|
+
|
|
132
134
|
# Define helper methods to define models
|
|
133
135
|
module Accel # rubocop:disable Metrics/ModuleLength
|
|
134
136
|
# @return [Hash]
|
|
@@ -567,7 +569,9 @@ module RASN2
|
|
|
567
569
|
# @return [Integer] number of parsed bytes
|
|
568
570
|
# @raise [ASN1Error] error on parsing
|
|
569
571
|
def parse!(der, ber: false)
|
|
570
|
-
root.parse!(der, ber: ber)
|
|
572
|
+
result = root.parse!(der, ber: ber)
|
|
573
|
+
result.model = self if result.respond_to? :model=
|
|
574
|
+
result
|
|
571
575
|
end
|
|
572
576
|
|
|
573
577
|
# @private
|
|
@@ -634,7 +638,7 @@ module RASN2
|
|
|
634
638
|
|
|
635
639
|
# @return [String]
|
|
636
640
|
def inspect(level=0, color: true)
|
|
637
|
-
|
|
641
|
+
root.inspect(level, color: color, kind: type)
|
|
638
642
|
end
|
|
639
643
|
|
|
640
644
|
# Objects are equal if they have same class AND same DER
|
data/lib/rasn2/schema_parser.rb
CHANGED
data/lib/rasn2/tracer.rb
CHANGED
|
@@ -37,7 +37,8 @@ module RASN2
|
|
|
37
37
|
# @param [String] msg
|
|
38
38
|
# @return [void]
|
|
39
39
|
def trace(msg)
|
|
40
|
-
|
|
40
|
+
indented = msg.split("\n").map { |line| indent + line }.join("\n")
|
|
41
|
+
@io.puts(indented)
|
|
41
42
|
end
|
|
42
43
|
|
|
43
44
|
# Return identation for given +level+. If +nil+, use {#tracing_level}.
|
|
@@ -45,7 +46,7 @@ module RASN2
|
|
|
45
46
|
# @return [String]
|
|
46
47
|
def indent(level=nil)
|
|
47
48
|
level ||= @tracing_level
|
|
48
|
-
|
|
49
|
+
" " * level
|
|
49
50
|
end
|
|
50
51
|
end
|
|
51
52
|
|
data/lib/rasn2/types/any.rb
CHANGED
|
@@ -31,67 +31,51 @@ module RASN2
|
|
|
31
31
|
# @param [Boolean] ber if +true+, accept BER encoding
|
|
32
32
|
# @return [Integer] total number of parsed bytes
|
|
33
33
|
def parse!(der, ber: false)
|
|
34
|
-
total_length,
|
|
34
|
+
total_length, data = do_parse(der, ber: ber)
|
|
35
|
+
@value = data
|
|
35
36
|
total_length
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
# @param [::Integer] level
|
|
39
|
-
# @return [String]
|
|
40
|
-
def inspect(level=0)
|
|
41
|
-
str = common_inspect(level)
|
|
42
|
-
str << if !value?
|
|
43
|
-
'NULL'
|
|
44
|
-
elsif @value.is_a?(OctetString) || @value.is_a?(BitString)
|
|
45
|
-
"#{@value.type}: #{value.value.inspect}"
|
|
46
|
-
elsif @value.class < Base
|
|
47
|
-
"#{@value.type}: #{value.value}"
|
|
48
|
-
else
|
|
49
|
-
value.to_s.inspect
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
39
|
# @private Tracer private API
|
|
54
40
|
# @return [String]
|
|
55
41
|
def trace
|
|
56
42
|
return trace_any if value?
|
|
57
43
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def common_inspect(level)
|
|
64
|
-
lvl = [0, level].max
|
|
65
|
-
str = ' ' * lvl
|
|
66
|
-
str << "#{@name} " unless @name.nil?
|
|
67
|
-
str << asn1_class.to_s.upcase << ' ' unless asn1_class == :universal
|
|
68
|
-
str << "[#{id}] EXPLICIT " if explicit?
|
|
69
|
-
str << "[#{id}] IMPLICIT " if implicit?
|
|
70
|
-
str << '(ANY) '
|
|
44
|
+
parts = []
|
|
45
|
+
parts << msg_type(no_id: true)
|
|
46
|
+
parts << colorize_nil
|
|
47
|
+
parts.reject(&:empty?).join(' ')
|
|
71
48
|
end
|
|
72
49
|
|
|
73
50
|
# @private
|
|
74
51
|
# @see Types::Base#do_parse
|
|
75
52
|
def do_parse(der, ber: false)
|
|
53
|
+
asn1_class, _pc, id, id_size = Types.decode_identifier_octets(der)
|
|
54
|
+
@id = id
|
|
55
|
+
@asn1_class = asn1_class
|
|
56
|
+
|
|
76
57
|
if der.empty?
|
|
77
58
|
return [0, ''] if optional?
|
|
78
59
|
|
|
79
60
|
raise ASN1Error, 'Expected ANY but get nothing'
|
|
80
61
|
end
|
|
81
62
|
|
|
82
|
-
id_size = Types.decode_identifier_octets(der).last
|
|
83
63
|
total_length, = get_data(der[id_size..], ber)
|
|
84
64
|
total_length += id_size
|
|
85
65
|
|
|
86
66
|
@no_value = false
|
|
87
67
|
real_value = der[0, total_length].to_s
|
|
88
|
-
@value =
|
|
68
|
+
@value = if constructed?
|
|
69
|
+
RASN2.parse(real_value, ber: ber)
|
|
70
|
+
else
|
|
71
|
+
real_value
|
|
72
|
+
end
|
|
89
73
|
|
|
90
|
-
[total_length,
|
|
74
|
+
[total_length, @value]
|
|
91
75
|
end
|
|
92
76
|
|
|
93
77
|
def trace_any
|
|
94
|
-
msg_type(no_id:
|
|
78
|
+
"#{msg_type(no_id: false)}#{trace_data(value)}"
|
|
95
79
|
end
|
|
96
80
|
end
|
|
97
81
|
end
|
data/lib/rasn2/types/base.rb
CHANGED
|
@@ -45,6 +45,10 @@ module RASN2
|
|
|
45
45
|
# ctype_implicit = RASN2::Types::Integer.new(implicit: 0)
|
|
46
46
|
# @author Sylvain Daubert
|
|
47
47
|
class Base # rubocop:disable Metrics/ClassLength
|
|
48
|
+
ID = nil
|
|
49
|
+
|
|
50
|
+
attr_accessor :model
|
|
51
|
+
|
|
48
52
|
# Allowed ASN.1 classes
|
|
49
53
|
CLASSES = {
|
|
50
54
|
universal: 0x00,
|
|
@@ -54,7 +58,7 @@ module RASN2
|
|
|
54
58
|
}.freeze
|
|
55
59
|
|
|
56
60
|
def colorize(msg)
|
|
57
|
-
tracer ? tracer.colorize(msg) :
|
|
61
|
+
tracer ? tracer.colorize(msg) : self.colorizer.wrap(msg)
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
include RASN2::Helpers::Colorize
|
|
@@ -199,6 +203,10 @@ module RASN2
|
|
|
199
203
|
defined?(@tag) ? @tag == :implicit : nil # rubocop:disable Style/ReturnNilInPredicateMethodDefinition
|
|
200
204
|
end
|
|
201
205
|
|
|
206
|
+
def private?
|
|
207
|
+
self.instance_of?(Types::Tag)
|
|
208
|
+
end
|
|
209
|
+
|
|
202
210
|
# @abstract This method SHOULD be partly implemented by subclasses, which
|
|
203
211
|
# SHOULD respond to +#value_to_der+.
|
|
204
212
|
# @return [String] DER-formated string
|
|
@@ -254,14 +262,43 @@ module RASN2
|
|
|
254
262
|
value_to_der.size
|
|
255
263
|
end
|
|
256
264
|
|
|
265
|
+
def attributes
|
|
266
|
+
attributes = []
|
|
267
|
+
attributes << 'OPTIONAL' if optional?
|
|
268
|
+
attributes << 'CONSTRUCTED' if constructed? && !(self.class < Constructed)
|
|
269
|
+
attributes << 'EXPLICIT' if explicit?
|
|
270
|
+
attributes << 'IMPLICIT' if implicit?
|
|
271
|
+
attributes
|
|
272
|
+
end
|
|
273
|
+
|
|
257
274
|
# @param [Integer] level
|
|
258
275
|
# @return [String]
|
|
259
|
-
def inspect(level=0, color:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
276
|
+
def inspect(level=0, color: false)
|
|
277
|
+
parts = []
|
|
278
|
+
new_level = level.abs + 1
|
|
279
|
+
|
|
280
|
+
begin_colorizer(color)
|
|
281
|
+
parts << common_inspect(level, color: color)
|
|
282
|
+
|
|
283
|
+
if constructed?
|
|
284
|
+
if @value.is_a?(Array)
|
|
285
|
+
@value.each do |value|
|
|
286
|
+
parts << "\n"
|
|
287
|
+
parts << value.inspect(new_level, color: color).to_s
|
|
288
|
+
end
|
|
289
|
+
else
|
|
290
|
+
parts << "\n"
|
|
291
|
+
if @value.is_a?(Base)
|
|
292
|
+
parts << @value.inspect(new_level, color: color).to_s
|
|
293
|
+
else
|
|
294
|
+
@value.inspect.to_s
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
else
|
|
298
|
+
parts << inspect_value unless inspect_value.empty?
|
|
299
|
+
end
|
|
300
|
+
end_colorizer
|
|
301
|
+
parts.reject(&:empty?).join(' ')
|
|
265
302
|
end
|
|
266
303
|
|
|
267
304
|
# Objects are equal if they have same class AND same DER
|
|
@@ -303,12 +340,14 @@ module RASN2
|
|
|
303
340
|
def trace
|
|
304
341
|
return trace_real if value?
|
|
305
342
|
|
|
306
|
-
|
|
307
|
-
if default.nil?
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
343
|
+
parts = [msg_type]
|
|
344
|
+
parts << if default.nil?
|
|
345
|
+
colorize_nil
|
|
346
|
+
else
|
|
347
|
+
colorize_default(value)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
parts.reject(&:empty?).join(' ')
|
|
312
351
|
end
|
|
313
352
|
|
|
314
353
|
# @private Parse tage and length from binary string. Return data length and binary data.
|
|
@@ -346,42 +385,28 @@ module RASN2
|
|
|
346
385
|
@value = der
|
|
347
386
|
end
|
|
348
387
|
|
|
349
|
-
private
|
|
350
|
-
|
|
351
388
|
def trace_real
|
|
352
|
-
|
|
389
|
+
raw_id = encode_identifier_octets
|
|
390
|
+
encoded_id = raw_id.unpack1('w*')
|
|
353
391
|
data_length = raw_data.length
|
|
354
|
-
encoded_length =
|
|
355
|
-
|
|
392
|
+
encoded_length = raw_length.unpack1('w*')
|
|
393
|
+
parts = [msg_type(raw_id.unpack1('H*').to_i(16), encoded_id)]
|
|
394
|
+
msg = parts.reject(&:empty?).join(' ')
|
|
395
|
+
msg << ", #{length_specifier(data_length, encoded_length)}"
|
|
356
396
|
msg << trace_data
|
|
357
397
|
end
|
|
358
398
|
|
|
359
399
|
def trace_data(data=nil)
|
|
360
|
-
byte_count = 0
|
|
361
400
|
data ||= raw_data
|
|
362
|
-
|
|
363
|
-
lines =
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
end
|
|
370
|
-
str[compute_trace_index(byte_count, 3), 2] = '%02x' % byte
|
|
371
|
-
str[compute_trace_index(byte_count, 1, 49)] = byte.between?(32, 126) ? byte.chr : '.'
|
|
372
|
-
byte_count += 1
|
|
401
|
+
format_string = data.size > 65_535 ? '%08X' : '%04X'
|
|
402
|
+
lines = data.bytes.each_slice(16).map.with_index do |chunk, index|
|
|
403
|
+
offset = format_string % (index * 16)
|
|
404
|
+
hex = chunk.map { |b| '%02x' % b }.join(' ')
|
|
405
|
+
hex_padded = hex.ljust(47) # 16*2 hex digits + 15 spaces
|
|
406
|
+
ascii = chunk.map { |b| (32..126).cover?(b) ? b.chr : '.' }.join
|
|
407
|
+
" #{offset} #{hex_padded} |#{ascii}|"
|
|
373
408
|
end
|
|
374
|
-
lines.
|
|
375
|
-
end
|
|
376
|
-
|
|
377
|
-
def trace_format_new_data_line(count)
|
|
378
|
-
head_line = RASN2.tracer.indent(RASN2.tracer.tracing_level + 1)
|
|
379
|
-
("\n#{head_line}%04x " % count) << ' ' * 68
|
|
380
|
-
end
|
|
381
|
-
|
|
382
|
-
def compute_trace_index(byte_count, byte_count_mul=1, offset=0)
|
|
383
|
-
base_idx = 7 + RASN2.tracer.indent(RASN2.tracer.tracing_level + 1).length
|
|
384
|
-
base_idx + offset + (byte_count % 16) * byte_count_mul
|
|
409
|
+
"\n#{lines.join("\n")}"
|
|
385
410
|
end
|
|
386
411
|
|
|
387
412
|
def unpack(binstr)
|
|
@@ -389,22 +414,16 @@ module RASN2
|
|
|
389
414
|
end
|
|
390
415
|
|
|
391
416
|
def asn1_class_to_s
|
|
392
|
-
asn1_class
|
|
417
|
+
asn1_class.to_s.upcase
|
|
393
418
|
end
|
|
394
419
|
|
|
395
|
-
def msg_type(no_id: false)
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
else
|
|
403
|
-
+''
|
|
404
|
-
end
|
|
405
|
-
msg << colorize_class(type)
|
|
406
|
-
msg << " #{colorize_attribute('OPTIONAL')}" if optional?
|
|
407
|
-
msg
|
|
420
|
+
def msg_type(raw_id=nil, _encoded_id=nil, no_id: false)
|
|
421
|
+
parts = []
|
|
422
|
+
parts << colorize_name(name) unless name.nil?
|
|
423
|
+
real_id = no_id ? nil : raw_id || id
|
|
424
|
+
parts << colorize_id(id, asn1_class_to_s) if real_id
|
|
425
|
+
parts << colorize_class(type, real_id, *self.attributes)
|
|
426
|
+
parts.reject(&:empty?).join(' ')
|
|
408
427
|
end
|
|
409
428
|
|
|
410
429
|
def pc_bit
|
|
@@ -417,22 +436,32 @@ module RASN2
|
|
|
417
436
|
end
|
|
418
437
|
end
|
|
419
438
|
|
|
420
|
-
def
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
439
|
+
def universal?
|
|
440
|
+
@asn1_class == :universal
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
def common_inspect(level=0, color: false)
|
|
444
|
+
begin_colorizer(color)
|
|
445
|
+
parts = []
|
|
446
|
+
parts << "(#{model})" if model
|
|
447
|
+
parts << colorize_name(name) if name
|
|
448
|
+
parts << colorize_id(id, asn1_class_to_s) if id && !universal?
|
|
449
|
+
class_name = colorize_class(type, nil, *self.attributes)
|
|
450
|
+
class_name += ':' if constructed?
|
|
451
|
+
parts << class_name
|
|
452
|
+
end_colorizer
|
|
453
|
+
"#{" " * level}" + parts.reject(&:empty?).join(' ').to_s
|
|
428
454
|
end
|
|
429
455
|
|
|
430
456
|
def inspect_value
|
|
431
|
-
if value?
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
457
|
+
str = if value?
|
|
458
|
+
colorize_value(value.inspect)
|
|
459
|
+
else
|
|
460
|
+
colorize_nil('(NO VALUE)')
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
str += colorize_default(@default) if @default
|
|
464
|
+
str
|
|
436
465
|
end
|
|
437
466
|
|
|
438
467
|
def value_to_der
|
|
@@ -586,6 +615,8 @@ module RASN2
|
|
|
586
615
|
elsif !@default.nil?
|
|
587
616
|
@value = @default
|
|
588
617
|
else
|
|
618
|
+
return true if self.instance_of?(Tag)
|
|
619
|
+
|
|
589
620
|
raise_id_error(der)
|
|
590
621
|
end
|
|
591
622
|
false
|
|
@@ -638,18 +669,24 @@ module RASN2
|
|
|
638
669
|
|
|
639
670
|
def raise_id_error(der)
|
|
640
671
|
msg = name.nil? ? +'' : "#{name}: "
|
|
641
|
-
msg << "Expected #{self2name} but
|
|
672
|
+
msg << "Expected #{self2name} but got #{der2name(der)}"
|
|
642
673
|
|
|
643
674
|
raise ASN1Error, msg
|
|
644
675
|
end
|
|
645
676
|
|
|
646
677
|
def self2name
|
|
647
|
-
|
|
678
|
+
parts = [asn1_class.to_s.upcase]
|
|
679
|
+
parts << (constructed? ? 'CONSTRUCTED' : 'PRIMITIVE').to_s
|
|
648
680
|
if implicit? || explicit?
|
|
649
|
-
|
|
681
|
+
parts << '0x%X (0x%s)' % [id, bin2hex(encode_identifier_octets)]
|
|
650
682
|
else
|
|
651
|
-
|
|
683
|
+
parts << self.class.type.to_s
|
|
652
684
|
end
|
|
685
|
+
parts.reject(&:empty?).join(' ')
|
|
686
|
+
end
|
|
687
|
+
|
|
688
|
+
def indent(input, by=2)
|
|
689
|
+
input.split("\n").map { |line| (" " * by) + line }.join("\n")
|
|
653
690
|
end
|
|
654
691
|
|
|
655
692
|
def der2name(der)
|
|
@@ -658,11 +695,12 @@ module RASN2
|
|
|
658
695
|
asn1_class, pc, id, id_size = Types.decode_identifier_octets(der)
|
|
659
696
|
name = "#{asn1_class.to_s.upcase} #{pc.to_s.upcase}"
|
|
660
697
|
type = find_type(id)
|
|
661
|
-
name << " #{type.nil? ?
|
|
698
|
+
name << " #{type.nil? ? bin2hex(der[0...id_size]).to_s : type.encoded_type}"
|
|
662
699
|
end
|
|
663
700
|
|
|
664
701
|
def find_type(id)
|
|
665
702
|
Types.constants.map { |c| Types.const_get(c) }
|
|
703
|
+
.grep(Class)
|
|
666
704
|
.select { |klass| klass < Primitive || klass < Constructed }
|
|
667
705
|
.find { |klass| id == klass::ID }
|
|
668
706
|
end
|
|
@@ -34,11 +34,8 @@ module RASN2
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
def inspect(level=0)
|
|
40
|
-
str = common_inspect(level)
|
|
41
|
-
str << " #{value.inspect} (bit length: #{bit_length})"
|
|
37
|
+
def inspect_value
|
|
38
|
+
"(bit length: #{bit_length}): #{value.inspect}"
|
|
42
39
|
end
|
|
43
40
|
|
|
44
41
|
# Same as {Base#can_build?} but also check bit_length
|
data/lib/rasn2/types/boolean.rb
CHANGED
data/lib/rasn2/types/choice.rb
CHANGED
|
@@ -126,12 +126,12 @@ module RASN2
|
|
|
126
126
|
|
|
127
127
|
# @param [::Integer] level
|
|
128
128
|
# @return [String]
|
|
129
|
-
def inspect(level=0)
|
|
129
|
+
def inspect(level=0, color: true)
|
|
130
130
|
str = common_inspect(level)
|
|
131
131
|
str << if defined?(@chosen) && value?
|
|
132
|
-
|
|
132
|
+
@value[@chosen].inspect(level + 1)
|
|
133
133
|
else
|
|
134
|
-
'
|
|
134
|
+
'not chosen!'
|
|
135
135
|
end
|
|
136
136
|
end
|
|
137
137
|
|
|
@@ -23,29 +23,6 @@ module RASN2
|
|
|
23
23
|
(el.value.respond_to?(:empty?) ? !el.value.empty? : !el.value.nil?))
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
|
-
|
|
27
|
-
# @param [::Integer] level (default: 0)
|
|
28
|
-
# @return [String]
|
|
29
|
-
def inspect(level=0)
|
|
30
|
-
case @value
|
|
31
|
-
when Array
|
|
32
|
-
str = common_inspect(level)
|
|
33
|
-
str << "\n"
|
|
34
|
-
level = level.abs + 1
|
|
35
|
-
@value.each do |item|
|
|
36
|
-
case item
|
|
37
|
-
when Base, Model, Wrapper
|
|
38
|
-
str << "#{item.inspect(level)}\n"
|
|
39
|
-
else
|
|
40
|
-
str << ' ' * level
|
|
41
|
-
str << "#{item.inspect}\n"
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
str
|
|
45
|
-
else
|
|
46
|
-
super
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
26
|
end
|
|
50
27
|
end
|
|
51
28
|
end
|
data/lib/rasn2/types/integer.rb
CHANGED
|
@@ -155,8 +155,9 @@ module RASN2
|
|
|
155
155
|
|
|
156
156
|
def trace_data
|
|
157
157
|
return super if explicit?
|
|
158
|
+
|
|
158
159
|
i = der_to_int_value(raw_data)
|
|
159
|
-
return " #{int_with_hex(i, raw_data.unpack1('
|
|
160
|
+
return " #{int_with_hex(i, raw_data.unpack1('w*'))}" if @enum.empty?
|
|
160
161
|
|
|
161
162
|
v = int_to_enum(i, check_enum: false)
|
|
162
163
|
" #{colorize_enum(v, raw_data)}"
|
data/lib/rasn2/types/null.rb
CHANGED
|
@@ -9,10 +9,11 @@ module RASN2
|
|
|
9
9
|
ID = 0x05
|
|
10
10
|
|
|
11
11
|
# @return [String]
|
|
12
|
-
def inspect(level=0)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
def inspect(level=0, color: true)
|
|
13
|
+
begin_colorizer(color)
|
|
14
|
+
result = common_inspect(level)
|
|
15
|
+
end_colorizer
|
|
16
|
+
result
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# @return [Boolean]
|
|
@@ -15,11 +15,8 @@ module RASN2
|
|
|
15
15
|
# OctetString id value
|
|
16
16
|
ID = 4
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def inspect(level=0)
|
|
21
|
-
str = common_inspect(level)
|
|
22
|
-
str << " #{value.inspect}"
|
|
18
|
+
def inspect_value
|
|
19
|
+
value.inspect
|
|
23
20
|
end
|
|
24
21
|
|
|
25
22
|
# Make string value from +der+ string. Force encoding to UTF-8
|
|
@@ -107,22 +107,17 @@ module RASN2
|
|
|
107
107
|
|
|
108
108
|
# @param [::Integer] level
|
|
109
109
|
# @return [String]
|
|
110
|
-
def inspect(level=0)
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
str << item.inspect(level)
|
|
120
|
-
str << "\n" unless str.end_with?("\n")
|
|
121
|
-
else
|
|
122
|
-
str << (' ' * level) << "#{item.inspect}\n"
|
|
123
|
-
end
|
|
110
|
+
def inspect(level=0, color: false)
|
|
111
|
+
new_level = level.abs + 1
|
|
112
|
+
begin_colorizer(color)
|
|
113
|
+
name = @name ? "#{colorize_name(@name)} " : ''
|
|
114
|
+
lines = []
|
|
115
|
+
lines << "#{" " * level}#{name}SEQUENCE OF:"
|
|
116
|
+
lines += @value.map do |item|
|
|
117
|
+
item.inspect(new_level, color: color)
|
|
124
118
|
end
|
|
125
|
-
|
|
119
|
+
end_colorizer
|
|
120
|
+
lines.join("\n")
|
|
126
121
|
end
|
|
127
122
|
|
|
128
123
|
# Make sequence of value from +der+ string
|
data/lib/rasn2/types/tag.rb
CHANGED
|
@@ -24,7 +24,7 @@ module RASN2
|
|
|
24
24
|
# generic = Tag.new(tag: :private, value: [Integer.new, OctetString.new])
|
|
25
25
|
# # or equivalently
|
|
26
26
|
# generic = Tag.new(any_private: true, value: [Integer.new])
|
|
27
|
-
class Tag <
|
|
27
|
+
class Tag < Base
|
|
28
28
|
# Placeholder ID — not used directly; tag is always dynamic.
|
|
29
29
|
ID = 0
|
|
30
30
|
|
|
@@ -72,22 +72,6 @@ module RASN2
|
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
|
|
75
|
-
# Parse constructed content from DER
|
|
76
|
-
# @param [String] der
|
|
77
|
-
# @param [Boolean] ber
|
|
78
|
-
# @return [void]
|
|
79
|
-
def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
|
|
80
|
-
if @value.is_a?(Array) && !@value.empty?
|
|
81
|
-
nb_bytes = 0
|
|
82
|
-
@value.each do |element|
|
|
83
|
-
nb_bytes += element.parse!(der[nb_bytes..])
|
|
84
|
-
end
|
|
85
|
-
else
|
|
86
|
-
@value = der
|
|
87
|
-
der.length
|
|
88
|
-
end
|
|
89
|
-
end
|
|
90
|
-
|
|
91
75
|
private
|
|
92
76
|
|
|
93
77
|
def extract_tag_binding(options)
|
|
@@ -114,6 +98,12 @@ module RASN2
|
|
|
114
98
|
end
|
|
115
99
|
|
|
116
100
|
def check_id(der)
|
|
101
|
+
asn1_class, pc, tag_id, size = Types.decode_identifier_octets(der)
|
|
102
|
+
@asn1_class = asn1_class
|
|
103
|
+
@constructed = pc
|
|
104
|
+
@tag = tag_id
|
|
105
|
+
@tag_size = size
|
|
106
|
+
|
|
117
107
|
return check_any_private_id(der) if @any_private && @accepted_tags.empty?
|
|
118
108
|
return check_multi_tag_id(der) if @accepted_tags.length > 1
|
|
119
109
|
|
|
@@ -123,10 +113,7 @@ module RASN2
|
|
|
123
113
|
def check_any_private_id(der)
|
|
124
114
|
return no_match(der) if der.nil? || der.empty?
|
|
125
115
|
|
|
126
|
-
|
|
127
|
-
asn1_class_bits = first_octet & CLASS_MASK
|
|
128
|
-
if asn1_class_bits == CLASSES[:private]
|
|
129
|
-
_, _, tag_id, = Types.decode_identifier_octets(der)
|
|
116
|
+
if asn1_class == :private
|
|
130
117
|
@id_value = tag_id
|
|
131
118
|
@matched_tag = tag_id
|
|
132
119
|
true
|
|
@@ -164,15 +151,13 @@ module RASN2
|
|
|
164
151
|
|
|
165
152
|
def value_to_der
|
|
166
153
|
case @value
|
|
167
|
-
when Array
|
|
168
|
-
|
|
154
|
+
when Array
|
|
155
|
+
@value.map(&:to_der).join
|
|
156
|
+
else
|
|
157
|
+
@value.to_s
|
|
169
158
|
end
|
|
170
159
|
end
|
|
171
160
|
|
|
172
|
-
def trace_data
|
|
173
|
-
''
|
|
174
|
-
end
|
|
175
|
-
|
|
176
161
|
def explicit_type
|
|
177
162
|
opts = { name: name, value: @value }
|
|
178
163
|
if @accepted_tags.length == 1
|
data/lib/rasn2/types.rb
CHANGED
|
@@ -15,6 +15,7 @@ module RASN2
|
|
|
15
15
|
return @primitives unless @primitives.empty?
|
|
16
16
|
|
|
17
17
|
@primitives = self.constants.map { |c| Types.const_get(c) }
|
|
18
|
+
.grep(Class)
|
|
18
19
|
.select { |klass| klass < Primitive }
|
|
19
20
|
end
|
|
20
21
|
|
|
@@ -24,6 +25,7 @@ module RASN2
|
|
|
24
25
|
return @constructed unless @constructed.empty?
|
|
25
26
|
|
|
26
27
|
@constructed = self.constants.map { |c| Types.const_get(c) }
|
|
28
|
+
.grep(Class)
|
|
27
29
|
.select { |klass| klass < Constructed }
|
|
28
30
|
end
|
|
29
31
|
|
|
@@ -67,10 +69,10 @@ module RASN2
|
|
|
67
69
|
asn1class, pc, id, = self.decode_identifier_octets(der)
|
|
68
70
|
# cache_id: check versus class and 5 LSB bits
|
|
69
71
|
cache_id = der.unpack1('C') & 0xdf
|
|
70
|
-
klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::
|
|
72
|
+
klass = cache_id < Types::Base::MULTI_OCTETS_ID ? @id2types[id] : Types::Tag
|
|
71
73
|
is_constructed = (pc == :constructed)
|
|
72
74
|
options = { class: asn1class, constructed: is_constructed }
|
|
73
|
-
options[:tag_value] = id if klass == Types::
|
|
75
|
+
options[:tag_value] = id if klass == Types::Tag
|
|
74
76
|
klass.new(options)
|
|
75
77
|
end
|
|
76
78
|
|
data/lib/rasn2/version.rb
CHANGED
data/lib/rasn2/wrapper.rb
CHANGED
|
@@ -201,13 +201,6 @@ module RASN2
|
|
|
201
201
|
!constructed?
|
|
202
202
|
end
|
|
203
203
|
|
|
204
|
-
# @param [::Integer] level
|
|
205
|
-
# @return [String]
|
|
206
|
-
def inspect(level=0)
|
|
207
|
-
return super() unless explicit?
|
|
208
|
-
|
|
209
|
-
@explicit_wrapper.inspect(level) << ' ' << super()
|
|
210
|
-
end
|
|
211
204
|
|
|
212
205
|
private
|
|
213
206
|
|
data/lib/rasn2.rb
CHANGED
|
@@ -35,7 +35,7 @@ module RASN2
|
|
|
35
35
|
type = Types.id2type(der)
|
|
36
36
|
size = type.parse!(der, ber: ber)
|
|
37
37
|
|
|
38
|
-
if CONTAINER_CLASSES.include?(type.class)
|
|
38
|
+
if CONTAINER_CLASSES.include?(type.class) || type.constructed?
|
|
39
39
|
RASN2.tracer.tracing_level += 1 unless RASN2.tracer.nil?
|
|
40
40
|
content = self.parse(type.value)
|
|
41
41
|
type.value = content.is_a?(Array) ? content : [content]
|