aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1kit/types.rb
blob: 817c842de75e66e20418191375fb0ac1136008be (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# coding: ASCII-8BIT
using ASN1Kit::BERString

module ASN1Kit
  class Type
    CONSTRAINT = nil

    class Tag < Struct.new(:tagging, :tag_class, :tag_number)
      def acceptable_ber_header?(str, offset = 0)
        tc, tn, constructed, hlen, len = str.get_object(offset)
        if tc == tag_class && tn == tag_number
          [constructed, hlen, len]
        else
          nil
        end
      end
    end
    TAG = nil

    class << self
      def tagging!(tagging, tag_class, tag_number)
        const_set(:TAG, Tag.new(tagging, tag_class, tag_number))
      end

      def asn1_tag(tagging, tag_class, tag_number)
        const_set(:TAG, Tag.new(tagging, tag_class, tag_number))
      end

      def asn1_alias(name)
        instance_variable_set(:@alias, name)
      end

      def tagging(tagging, tag_class, tag_number)
        ret = Class.new(self)
        ret.tagging!(tagging, tag_class, tag_number)
        ret
      end

      def from_ber(str)
        list = self::TAG.acceptable_ber_header?(str) or
          raise EncodingError, "invalid object: %p, %p, %p, %p, %p" % str.get_object(str)

        constructed, hlen, len = list
        if self::TAG.tagging == :EXPLICIT
          raise EncodingError, "invalid EXPLICIT tagging encoding" unless constructed
          c = self
          while c = c.superclass
            raise EncodingError, "invalid EXPLICIT tagging: %p" % self unless c < ASN1Kit::Type
            break unless c::TAG.equal?(self::TAG)
          end
          list_inner = c::TAG.acceptable_ber_header?(str, hlen) or
            raise EncodingError, "invalid EXPLICIT tagging inner object"
        end

        if len == 0x80
          from_ber_content(str, 0, nil, true)
        else
          from_ber_content(str, 0, len, constructed)
        end
      end

      private def from_ber_content(str, offset, len, constructed)
        raise NotImplementedError, "BER decoding for %p not implemented" % self
      end

      def check_ber_header(str, expect_con = false, pos: 0)
        tc, con, tn, len, pos = str.get_object(pos)
        if tc != self::TAG.tag_class || expect_con != con || tn != self::TAG.tag_number
          raise "invalid object: %p, %p, %p, %p, %p" % [tc, con, tn, len, pos]
        end
        [len, pos]
      end

      def builtin?
        false
      end

      def inspect_abbrev
        defined?(@alias) ? "<#{@alias}>" : inspect
      end

      def inspect
        name = defined?(@alias) ? "(#{@alias}) " : ""
        type_ancestors = ancestors.drop(1).select { |x| x < Type }
        baseclass = type_ancestors.find { |x| x.instance_variable_defined?(:@alias) }
        baseclass ||= type_ancestors.reverse_each.find { |x| x < ASN1Kit::Type }
        if baseclass
          if self::TAG != baseclass::TAG
            prefix = "["
            if self::TAG.tag_class != :CONTEXT_SPECIFIC
              prefix << self::TAG.tag_class.to_s << " "
            end
            prefix << self::TAG.tag_number.to_s << "] "
          end
          bname = baseclass.instance_variable_get(:@alias) || baseclass.name
        end
        if body = inspect_inner
          "<#{name}#{prefix}#{bname} #{body}>"
        else
          "<#{name}#{prefix}#{bname}>"
        end
      end

      private def inspect_inner() nil end
    end

    def to_der
      raise NotImplementedError, "DER encoding for %p not implemented" % self.class
    end

    private def der_header(len, encoding = :primitive)
      "".put_object(self.class::TAG.tag_class, self.class::TAG.tag_number, len, encoding)
    end

    def cast_to(type)
      raise TypeError, "casting %p value into %p undefined" % [self.class, type]
    end

    def inspect_abbrev
      defined?(@alias) ? "{#{@alias}}" : inspect
    end

    def inspect
      name = defined?(@alias) ? "(#{@alias})" : ""
      if body = inspect_inner
        "{#{name}#{self.class.inspect_abbrev} #{body}}"
      else
        "{#{name}#{self.class.inspect_abbrev}}"
      end
    end

    def initialize_copy(orig)
      super
      # UGHH; Duplicated instances should not have an alias
      remove_instance_variable(:@alias) if defined?(@alias)
    end

    private def inspect_inner() defined?(@value) ? @value.inspect : nil end
  end
end

module ASN1Kit::Internal
  module CompileType
    refine ASN1Kit::Type.singleton_class do
      def _compile_fixup(state)
      end

      attr_reader :alias
      def alias=(value)
        unreachable if defined?(@alias)
        @alias = value
      end
    end

    refine ASN1Kit::Type do
      def _compile_fixup(state)
      end

      attr_reader :alias
      def alias=(value)
        unreachable if defined?(@alias)
        @alias = value
      end
    end
  end
end

require_relative "types/boolean"
require_relative "types/integer"
require_relative "types/bit_string"
require_relative "types/octet_string"
require_relative "types/null"
require_relative "types/object_identifier"
require_relative "types/real"
require_relative "types/enumerated"
require_relative "types/relative_oid"
require_relative "types/sequence"
require_relative "types/sequence_of"
require_relative "types/set"
require_relative "types/set_of"
require_relative "types/choice"
require_relative "types/character_string_types"
require_relative "types/useful_types"