aboutsummaryrefslogtreecommitdiffstats
path: root/lib/asn1kit/types/integer.rb
blob: 9104a5d47714fc01d6c16266bc9f4a47aef2290d (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
# coding: ASCII-8BIT

class ASN1Kit::Integer < ASN1Kit::Type
  asn1_tag :IMPLICIT, :UNIVERSAL, 2
  asn1_alias "INTEGER"

  NAMED_NUMBERS = {}
  NAMED_NUMBERS_INVERTED = {}

  class << self
    def [](*named_numbers)
      return self if named_numbers.empty?
      ret = Class.new(self)
      hash = named_numbers.to_h
      hash_inverted = hash.invert
      raise "duplicate name(s) in named number list" if named_numbers.size != hash.size
      raise "duplicate value(s) in named number list" if hash.size != hash_inverted.size
      ret.const_set(:NAMED_NUMBERS, hash)
      ret.const_set(:NAMED_NUMBERS_INVERTED, hash.invert)
      ret
    end

    def named_number(name)
      self::NAMED_NUMBERS[name]
    end

    def named_numbers
      self::NAMED_NUMBERS.keys
    end

    private def inspect_inner
      return nil if self::NAMED_NUMBERS.empty?
      nns = self::NAMED_NUMBERS.map { |name, value| "#{name}(#{value})" }.join(", ")
      "{ #{nns} }"
    end
  end

  attr_reader :value

  def initialize(value)
    case value
    when Integer
      @value = value
    when String
      @value = self.class::NAMED_NUMBERS[value] or
        raise ArgumentError, "invalid name %p" % value
    else
      raise TypeError, "invalid value: %p (expected an Integer or a NamedValue label)" % value
    end
  end

  def name
    self.class::NAMED_NUMBERS_INVERTED[@value]
  end

  def to_der
    v = @value
    if v >= 0
      dig = v.digits(256)
      dig << 0x00 if dig[-1] >= 0x80
      s = dig.pack("C*").reverse
    else
      v = ~v
      dig = v.digits(256)
      dig << 0x00 if dig[-1] >= 0x80
      s = dig.map!(&:~).pack("C*").reverse
    end
    der_header(s.bytesize) << s
  end

  def ==(other)
    other.class <= ASN1Kit::Integer && value == other.value
  end

  def cast_to(type)
    return type.new(@value) if type <= ASN1Kit::Integer
    super
  end

  private def inspect_inner
    if n = name
      "#{@value}(#{n})"
    else
      "#{@value}"
    end
  end
end