rasn2 0.16.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.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +4 -0
  4. data/lib/rasn1/errors.rb +55 -0
  5. data/lib/rasn1/helpers/colorize.rb +76 -0
  6. data/lib/rasn1/model.rb +831 -0
  7. data/lib/rasn1/schema_parser.rb +470 -0
  8. data/lib/rasn1/tracer.rb +200 -0
  9. data/lib/rasn1/types/any.rb +98 -0
  10. data/lib/rasn1/types/base.rb +675 -0
  11. data/lib/rasn1/types/bit_string.rb +100 -0
  12. data/lib/rasn1/types/bmp_string.rb +22 -0
  13. data/lib/rasn1/types/boolean.rb +57 -0
  14. data/lib/rasn1/types/choice.rb +158 -0
  15. data/lib/rasn1/types/constrained.rb +51 -0
  16. data/lib/rasn1/types/constructed.rb +51 -0
  17. data/lib/rasn1/types/enumerated.rb +44 -0
  18. data/lib/rasn1/types/generalized_time.rb +156 -0
  19. data/lib/rasn1/types/ia5_string.rb +21 -0
  20. data/lib/rasn1/types/integer.rb +166 -0
  21. data/lib/rasn1/types/null.rb +43 -0
  22. data/lib/rasn1/types/numeric_string.rb +41 -0
  23. data/lib/rasn1/types/object_id.rb +64 -0
  24. data/lib/rasn1/types/octet_string.rb +52 -0
  25. data/lib/rasn1/types/primitive.rb +13 -0
  26. data/lib/rasn1/types/printable_string.rb +42 -0
  27. data/lib/rasn1/types/sequence.rb +105 -0
  28. data/lib/rasn1/types/sequence_of.rb +199 -0
  29. data/lib/rasn1/types/set.rb +28 -0
  30. data/lib/rasn1/types/set_of.rb +18 -0
  31. data/lib/rasn1/types/tag.rb +189 -0
  32. data/lib/rasn1/types/universal_string.rb +22 -0
  33. data/lib/rasn1/types/utc_time.rb +67 -0
  34. data/lib/rasn1/types/utf8_string.rb +21 -0
  35. data/lib/rasn1/types/visible_string.rb +30 -0
  36. data/lib/rasn1/types.rb +149 -0
  37. data/lib/rasn1/value_notation.rb +256 -0
  38. data/lib/rasn1/version.rb +6 -0
  39. data/lib/rasn1/wrapper.rb +279 -0
  40. data/lib/rasn1.rb +51 -0
  41. metadata +123 -0
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # ASN.1 Bit String
6
+ # @author Sylvain Daubert
7
+ class BitString < Primitive
8
+ # BitString id value
9
+ ID = 3
10
+
11
+ # @return [Integer]
12
+ attr_writer :bit_length
13
+
14
+ # @param [Hash] options
15
+ # @option options [Object] :bit_length default bit_length value. Should be
16
+ # present if +:default+ is set
17
+ # @see Base#initialize common options to all ASN.1 types
18
+ def initialize(options={})
19
+ super
20
+ if @default
21
+ raise ASN1Error, "#{@name}: default bit length is not defined" if @options[:bit_length].nil?
22
+
23
+ @default_bit_length = @options[:bit_length]
24
+ end
25
+ @bit_length = @options[:bit_length]
26
+ end
27
+
28
+ # Get bit length
29
+ def bit_length
30
+ if value?
31
+ @bit_length
32
+ else
33
+ @default_bit_length
34
+ end
35
+ end
36
+
37
+ # @param [Integer] level
38
+ # @return [String]
39
+ def inspect(level=0)
40
+ str = common_inspect(level)
41
+ str << " #{value.inspect} (bit length: #{bit_length})"
42
+ end
43
+
44
+ # Same as {Base#can_build?} but also check bit_length
45
+ # @see Base#can_build?
46
+ # @return [Boolean]
47
+ def can_build?
48
+ super || (!@default.nil? && (@bit_length != @default_bit_length))
49
+ end
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
+
63
+ private
64
+
65
+ # @author Sylvain Daubert
66
+ # @author adfoster-r7
67
+ def value_to_der
68
+ raise ASN1Error, "#{@name}: bit length is not set" if bit_length.nil?
69
+
70
+ value = generate_value_with_correct_length
71
+
72
+ unused = value.length * 8 - @bit_length.to_i
73
+ der = [unused, value].pack('CA*')
74
+
75
+ if unused.positive?
76
+ last_byte = value[-1].to_s.unpack1('C').to_i
77
+ last_byte &= (0xff >> unused) << unused
78
+ der[-1] = [last_byte].pack('C')
79
+ end
80
+
81
+ @value = value
82
+
83
+ der
84
+ end
85
+
86
+ def generate_value_with_correct_length
87
+ value = (@value || '').dup.force_encoding('BINARY')
88
+ value << "\x00".b while value.length * 8 < @bit_length.to_i
89
+ return value unless value.length * 8 > @bit_length.to_i
90
+
91
+ max_len = @bit_length.to_i / 8 + ((@bit_length.to_i % 8).positive? ? 1 : 0)
92
+ value[0, max_len].to_s
93
+ end
94
+
95
+ def explicit_type
96
+ self.class.new(name: name, value: @value, bit_length: @bit_length)
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,22 @@
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
+ # UniversalString encoding
12
+ # @since 0.15.0
13
+ ENCODING = Encoding::UTF_16BE
14
+
15
+ # Get ASN.1 type
16
+ # @return [String]
17
+ def self.type
18
+ 'BmpString'
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # ASN.1 Boolean
6
+ # @author Sylvain Daubert
7
+ class Boolean < Primitive
8
+ # Boolean id value
9
+ ID = 0x01
10
+
11
+ # DER true value
12
+ DER_TRUE = 0xff
13
+ # DER false value
14
+ DER_FALSE = 0
15
+
16
+ # @return [false]
17
+ def void_value # rubocop:disable Naming/PredicateMethod
18
+ false
19
+ end
20
+
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+
28
+ def der_to_value(der, ber: false)
29
+ raise ASN1Error, "tag #{@name}: BOOLEAN should have a length of 1" unless der.size == 1
30
+
31
+ bool = der.unpack1('C')
32
+ case bool
33
+ when DER_FALSE
34
+ @value = false
35
+ when DER_TRUE
36
+ @value = true
37
+ else
38
+ raise ASN1Error, "tag #{@name}: bad value 0x%02x for BOOLEAN" % bool unless ber
39
+
40
+ @value = true
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def value_to_der
47
+ [@value ? DER_TRUE : DER_FALSE].pack('C')
48
+ end
49
+
50
+ def trace_data
51
+ return super if explicit?
52
+
53
+ ' ' + colorize_bool(raw_data != "\x00".b, raw_data.unpack1('H*'))
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # A ASN.1 CHOICE is a choice between different types.
6
+ #
7
+ # == Create a CHOICE
8
+ # A CHOICE is defined this way:
9
+ # choice = Choice.new
10
+ # choice.value = [Integer.new(implicit: 0, class: :context),
11
+ # Integer.new(implicit: 1, class: :context),
12
+ # OctetString.new(implicit: 2, class: :context)]
13
+ # The chosen type may be set this way:
14
+ # choice.chosen = 0 # choose :int1
15
+ # The chosen value may be set these ways:
16
+ # choise.value[choice.chosen].value = 1
17
+ # choise.set_chosen_value 1
18
+ # The chosen value may be got these ways:
19
+ # choise.value[choice.chosen].value # => 1
20
+ # choice.chosen_value # => 1
21
+ #
22
+ # == Encode a CHOICE
23
+ # {#to_der} only encodes the chosen value:
24
+ # choise.to_der # => "\x80\x01\x01"
25
+ #
26
+ # == Parse a CHOICE
27
+ # Parsing a CHOICE set {#chosen} and set value to chosen type. If parsed string does
28
+ # not contain a type from CHOICE, a {RASN1::ASN1Error} is raised.
29
+ # str = "\x04\x03abc"
30
+ # choice.parse! str
31
+ # choice.chosen # => 2
32
+ # choice.chosen_value # => "abc"
33
+ # @author Sylvain Daubert
34
+ class Choice < Base
35
+ # Chosen type
36
+ # @return [Integer] index of type in choice value
37
+ attr_accessor :chosen
38
+
39
+ # Set chosen value.
40
+ # @note {#chosen} MUST be set before calling this method
41
+ # @param [Object] value
42
+ # @return [Object] value
43
+ # @raise [ChoiceError] {#chosen} not set
44
+ def set_chosen_value(value) # rubocop:disable Naming/AccessorMethodName
45
+ check_chosen
46
+ @value[@chosen].value = value
47
+ end
48
+
49
+ # Get chosen value
50
+ # @note {#chosen} MUST be set before calling this method
51
+ # @return [Object] value
52
+ # @raise [ChoiceError] {#chosen} not set
53
+ def chosen_value
54
+ check_chosen
55
+ case @value[@chosen]
56
+ when Base
57
+ @value[@chosen].value
58
+ when Model
59
+ @value[@chosen]
60
+ when Wrapper
61
+ @value[@chosen].element
62
+ end
63
+ end
64
+
65
+ # @note {#chosen} MUST be set before calling this method
66
+ # @return [String] DER-formated string
67
+ # @raise [ChoiceError] {#chosen} not set
68
+ def to_der
69
+ check_chosen
70
+ @value[@chosen].to_der
71
+ end
72
+
73
+ # Parse a DER string. This method updates object by setting {#chosen} and
74
+ # chosen value.
75
+ # @param [String] der DER string
76
+ # @param [Boolean] ber if +true+, accept BER encoding
77
+ # @return [Integer] total number of parsed bytes
78
+ # @raise [ASN1Error] error on parsing
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)
96
+ @value.each_with_index do |element, i|
97
+ @chosen = i
98
+ return element.do_parse(der, ber: ber)
99
+ rescue ASN1Error
100
+ @chosen = nil
101
+ next
102
+ end
103
+
104
+ @no_value = true
105
+ @value = void_value
106
+ raise ASN1Error, "CHOICE #{@name}: no type matching #{der.inspect}" unless optional?
107
+
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
125
+ end
126
+
127
+ # @param [::Integer] level
128
+ # @return [String]
129
+ def inspect(level=0)
130
+ str = common_inspect(level)
131
+ str << if defined?(@chosen) && value?
132
+ "\n#{@value[@chosen].inspect(level + 1)}"
133
+ else
134
+ ' not chosen!'
135
+ end
136
+ end
137
+
138
+ # @private Tracer private API
139
+ # @return [String]
140
+ def trace
141
+ msg_type(no_id: true)
142
+ end
143
+
144
+ # Return empty array
145
+ # @return [Array()]
146
+ # @since 0.15.0
147
+ def void_value
148
+ []
149
+ end
150
+
151
+ private
152
+
153
+ def check_chosen
154
+ raise ChoiceError.new(self) if !defined?(@chosen) || @chosen.nil?
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # Mixin to add 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
+ # Define class/module methods for {Constrained} module
11
+ module ClassMethods
12
+ # @return [Proc] proc to check constraints
13
+ attr_accessor :constraint
14
+
15
+ # Check if a constraint is really defined
16
+ # @return [Boolean]
17
+ def constrained?
18
+ @constraint.is_a?(Proc)
19
+ end
20
+
21
+ # Check constraint, if defined
22
+ # @param [Object] value the value of the type to check
23
+ # @raise [ConstraintError] constraint is not verified
24
+ def check_constraint(value)
25
+ return unless constrained?
26
+ raise ConstraintError.new(self) unless @constraint.call(value)
27
+ end
28
+ end
29
+
30
+ extend ClassMethods
31
+
32
+ # Redefined +#value=+ to check constraint before assigning +val+
33
+ # @see Types::Base#value=
34
+ # @raise [ConstraintError] constraint is not verified
35
+ def value=(val)
36
+ self.class.check_constraint(val)
37
+ super
38
+ end
39
+
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
45
+ def der_to_value(der, ber: false)
46
+ super
47
+ self.class.check_constraint(@value)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # @abstract This class SHOULD be used as base class for all ASN.1 primitive
6
+ # types.
7
+ # Base class for all ASN.1 constructed types
8
+ # @author Sylvain Daubert
9
+ class Constructed < Base
10
+ # Constructed value
11
+ ASN1_PC = 0x20
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]
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
+ end
50
+ end
51
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # ASN.1 Enumerated
6
+ #
7
+ # An enumerated type permits to assign names to integer values. It may be defined
8
+ # different ways:
9
+ # enum = RASN1::Types::Enumerated.new(enum: { 'a' => 0, 'b' => 1, 'c' => 2 })
10
+ # enum = RASN1::Types::Enumerated.new(enum: { a: 0, b: 1, c: 2 })
11
+ # Its value should be setting as an Integer or a String/symbol:
12
+ # enum.value = :b
13
+ # enum.value = 1 # equivalent to :b
14
+ # But its value is always stored as named integer:
15
+ # enum.value = :b
16
+ # enum.value # => :b
17
+ # enum.value = 0
18
+ # enum.value # => :a
19
+ # A {EnumeratedError} is raised when set value is not in enumeration.
20
+ # @author Sylvain Daubert
21
+ class Enumerated < Integer
22
+ # @!attribute enum
23
+ # @return [Hash]
24
+
25
+ # Enumerated id value
26
+ ID = 0x0a
27
+
28
+ # @option options [Hash] :enum enumeration hash. Keys are names, and values
29
+ # are integers. This key is mandatory.
30
+ # @raise [EnumeratedError] +:enum+ key is not present
31
+ # @raise [EnumeratedError] +:default+ value is unknown
32
+ # @see Base#initialize common options to all ASN.1 types
33
+ def initialize(options={})
34
+ super
35
+ raise EnumeratedError, 'no enumeration given' if @enum.empty?
36
+ end
37
+
38
+ # @return [Hash]
39
+ def to_h
40
+ @enum
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,156 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'strptime'
4
+
5
+ module RASN1
6
+ module Types
7
+ # ASN.1 GeneralizedTime
8
+ #
9
+ # +{#value} of a {GeneralizedTime} should be a ruby Time.
10
+ #
11
+ # ===Notes
12
+ # When encoding, resulting string is always a UTC time, appended with +Z+.
13
+ # Minutes and seconds are always generated. Fractions of second are generated
14
+ # if value Time object have them.
15
+ #
16
+ # On parsing, are supported:
17
+ # * UTC times (ending with +Z+),
18
+ # * local times (no suffix),
19
+ # * local times with difference between UTC and this local time (ending with
20
+ # +sHHMM+, where +s+ is +++ or +-+, and +HHMM+ is the time differential
21
+ # betwen UTC and local time).
22
+ # These times may include minutes and seconds. Fractions of hour, minute and
23
+ # second are supported.
24
+ # @author Sylvain Daubert
25
+ class GeneralizedTime < Primitive
26
+ # GeneralizedTime id value
27
+ ID = 24
28
+
29
+ # @private
30
+ HOUR_TO_SEC = 3600
31
+ # @private
32
+ MINUTE_TO_SEC = 60
33
+ # @private
34
+ SECOND_TO_SEC = 1
35
+
36
+ # Get ASN.1 type
37
+ # @return [String]
38
+ def self.type
39
+ 'GeneralizedTime'
40
+ end
41
+
42
+ # @return [Time]
43
+ def void_value
44
+ Time.now
45
+ end
46
+
47
+ # Make time value from +der+ string
48
+ # @param [String] der
49
+ # @param [::Boolean] ber
50
+ # @return [void]
51
+ def der_to_value(der, ber: false) # rubocop:disable Lint/UnusedMethodArgument
52
+ date_hour, fraction = der.split('.')
53
+ date_hour = date_hour.to_s
54
+ fraction = fraction.to_s
55
+
56
+ if fraction.empty?
57
+ value_when_fraction_empty(date_hour)
58
+ elsif fraction[-1] == 'Z'
59
+ value_when_fraction_ends_with_z(date_hour, fraction)
60
+ else
61
+ value_on_others_cases(date_hour, fraction)
62
+ end
63
+ end
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
+
77
+ def value_when_fraction_empty(date_hour)
78
+ tz = compute_tz(date_hour)
79
+ year = date_hour.slice!(0, 4).to_i
80
+ month = date_hour.slice!(0, 2).to_i
81
+ day = date_hour.slice!(0, 2).to_i
82
+ hour = date_hour.slice!(0, 2).to_i
83
+ minute = date_hour.slice!(0, 2).to_i
84
+ second = date_hour.slice!(0, 2).to_i
85
+ @value = Time.new(year, month, day, hour, minute, second, tz)
86
+ end
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
+
103
+ def value_when_fraction_ends_with_z(date_hour, fraction)
104
+ fraction = fraction[0...-1]
105
+ date_hour << 'Z'
106
+ frac_base = compute_frac_base(date_hour)
107
+ value_when_fraction_empty(date_hour)
108
+ fix_value(fraction, frac_base)
109
+ end
110
+
111
+ def value_on_others_cases(date_hour, fraction)
112
+ match = fraction.match(/(\d+)([+-]\d+)/)
113
+ if match
114
+ # fraction contains fraction and timezone info. Split them
115
+ fraction = match[1]
116
+ date_hour << match[2]
117
+ end
118
+
119
+ frac_base = compute_frac_base(date_hour)
120
+ value_when_fraction_empty(date_hour)
121
+ fix_value(fraction, frac_base)
122
+ end
123
+
124
+ def fix_value(fraction, frac_base)
125
+ frac = ".#{fraction}".to_r * frac_base
126
+ @value = (@value + frac) unless fraction.nil?
127
+ end
128
+
129
+ def compute_frac_base(date_hour)
130
+ case date_hour.size
131
+ when 10, 11
132
+ HOUR_TO_SEC
133
+ when 12, 13, 17
134
+ MINUTE_TO_SEC
135
+ when 15
136
+ if date_hour[-1] == 'Z'
137
+ SECOND_TO_SEC
138
+ else
139
+ HOUR_TO_SEC
140
+ end
141
+ when 14, 19
142
+ SECOND_TO_SEC
143
+ else
144
+ prefix = @name.nil? ? type : "tag #{@name}"
145
+ raise ASN1Error, "#{prefix}: unrecognized format: #{date_hour}"
146
+ end
147
+ end
148
+
149
+ def trace_data
150
+ return super if explicit?
151
+
152
+ +' ' << colorize(Time.parse(raw_data)).dark.green
153
+ end
154
+ end
155
+ end
156
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RASN1
4
+ module Types
5
+ # ASN.1 IA5 String
6
+ # @author Sylvain Daubert
7
+ class IA5String < OctetString
8
+ # IA5String id value
9
+ ID = 22
10
+ # UniversalString encoding
11
+ # @since 0.15.0
12
+ ENCODING = Encoding::US_ASCII
13
+
14
+ # Get ASN.1 type
15
+ # @return [String]
16
+ def self.type
17
+ 'IA5String'
18
+ end
19
+ end
20
+ end
21
+ end