File: tag.py

package info (click to toggle)
golang-github-google-certificate-transparency 0.0~git20160709.0.0f6e3d1~ds1-3
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye, buster
  • size: 5,676 kB
  • sloc: cpp: 35,278; python: 11,838; java: 1,911; sh: 1,885; makefile: 950; xml: 520; ansic: 225
file content (134 lines) | stat: -rw-r--r-- 4,183 bytes parent folder | download
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
"""ASN.1 tagging."""

from ct.crypto import error


UNIVERSAL = 0x00
APPLICATION = 0x40
CONTEXT_SPECIFIC = 0x80
PRIVATE = 0xc0
PRIMITIVE = 0x00
CONSTRUCTED = 0x20

# Constants for better readability.
IMPLICIT, EXPLICIT = range(2)


class Tag(object):
    """An ASN.1 tag."""
    _CLASS_MASK = 0xc0
    _ENCODING_MASK = 0x20
    _NUMBER_MASK = 0x1f
    _HIGH = 0x1f
    _FULL_SUB_OCTET = 0x7f
    _LAST_OCTET = 0x80

    def __init__(self, number, tag_class, encoding):
        """ASN.1 tag.

        Initialize a tag from its number, class and encoding.

        Args:
            number: the numeric value of the tag.
            tag_class: must be one of UNIVERSAL, APPLICATION, CONTEXT_SPECIFIC
                or PRIVATE.
            encoding: must be one of PRIMITIVE or CONSTRUCTED.

        Raises:
            ValueError: invalid initializers.
        """
        if tag_class not in (UNIVERSAL, APPLICATION, CONTEXT_SPECIFIC, PRIVATE):
            raise ValueError("Invalid tag class %s" % tag_class)
        if encoding not in (PRIMITIVE, CONSTRUCTED):
            raise ValueError("Invalid encoding %s" % encoding)

        # Public just for lightweight access. Do not modify directly.
        self.number = number
        self.tag_class = tag_class
        self.encoding = encoding
        if number <= 30:
            self.value = chr(tag_class | encoding | number)
        else:
            res = [tag_class | encoding | self._HIGH]
            tmp = []
            while number > 0:
                tmp.append((number & self._FULL_SUB_OCTET) | self._LAST_OCTET)
                number >>= 7
            tmp[0] -= self._LAST_OCTET
            tmp.reverse()
            res += tmp
            self.value = ''.join([chr(byte) for byte in res])

    def __repr__(self):
        return ("%s(%r, %r, %r)" % (self.__class__.__name__, self.number,
                                    self.tag_class, self.encoding))

    def __str__(self):
        return "[%s %d]" % (self.class_name(), self.number)

    def __len__(self):
        return len(self.value)

    def class_name(self):
        if self.tag_class == UNIVERSAL:
            return "UNIVERSAL"
        elif self.tag_class == APPLICATION:
            return "APPLICATION"
        elif self.tag_class == CONTEXT_SPECIFIC:
            return "CONTEXT-SPECIFIC"
        elif self.tag_class == PRIVATE:
            return "PRIVATE"
        else:
            raise ValueError("Invalid tag class %x" % self.tag_class)

    def __hash__(self):
        return hash(self.value)

    def __eq__(self, other):
        if not isinstance(other, Tag):
            return NotImplemented
        return self.value == other.value

    def __ne__(self, other):
        if not isinstance(other, Tag):
            return NotImplemented
        return self.value != other.value

    @classmethod
    def read(cls, buf):
        """Read from the beginning of a string or buffer.

        Args:
            buf: a binary string or string buffer containing an ASN.1 object.

        Returns:
            an tuple consisting of an instance of the class and the remaining
            buffer/string.
        """

        if not buf:
            raise error.ASN1TagError("Ran out of bytes while decoding")
        tag_bytes = 0
        id_byte = ord(buf[tag_bytes])
        tag_class = id_byte & cls._CLASS_MASK
        encoding = id_byte & cls._ENCODING_MASK
        number = id_byte & cls._NUMBER_MASK
        if number == cls._HIGH:
            number = 0
            tag_bytes += 1
            success = False
            for i in range(1, len(buf)):
                number <<= 7
                id_byte = ord(buf[i])
                number |= (id_byte & cls._FULL_SUB_OCTET)
                tag_bytes += 1
                if id_byte & cls._LAST_OCTET == 0:
                    success = True
                    break
            if not success:
                raise error.ASN1TagError("Ran out of bytes while decoding")
            if tag_bytes - 1 > 5:
                raise error.ASN1TagError("Base 128 integer too large")
            tag_bytes -= 1
        tag = cls(number, tag_class, encoding)
        return tag, buf[tag_bytes + 1:]