spoon.ber.tag

Package spoon :: Package ber :: Module tag |
[hide private]
[frames] | no frames]

# Source Code for Module spoon.ber.tag

      1  #
      2  # Copyright (C) 2003-2006, Robey Pointer <robey@lag.net>
      3  #
      4  # Permission is hereby granted, free of charge, to any person obtaining
      5  # a copy of this software and associated documentation files (the
      6  # "Software"), to deal in the Software without restriction, including
      7  # without limitation the rights to use, copy, modify, merge, publish,
      8  # distribute, sublicense, and/or sell copies of the Software, and to
      9  # permit persons to whom the Software is furnished to do so, subject to
     10  # the following conditions:
     11  #
     12  # The above copyright notice and this permission notice shall be included
     13  # in all copies or substantial portions of the Software.
     14  #
     15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
     16  # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     17  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
     18  # IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
     19  # CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
     20  # TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
     21  # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     22  #
     23
     24
     25  from common import BERException, inflate_long, deflate_long
     26  from common import UNIVERSAL, APPLICATION, CONTEXT, PRIVATE
     27
     28
     29  _class_name = { UNIVERSAL: 'UNIVERSAL', APPLICATION: 'APPLICATION',
     30                  CONTEXT: 'CONTEXT', PRIVATE: 'PRIVATE' }
     31
     32
     33 -class Tag (object):
     34      """
     35      Representation of the header of an ASN.1 object.  This includes the
     36      class (universal, application, context, or private), the type tag (any
     37      integer), and size.
     38      """
     39
     40 -    def __init__(self, tag_class=0, tag=UNIVERSAL, size=None, container=False, _bytes_read=0):
     41          self._tag_class = tag_class
     42          self._tag = tag
     43          self._size = size
     44          self._container = container
     45          self._bytes_read = _bytes_read
     46
     47 -    def __repr__(self):
     48          return '<ASN.1 Tag(%s, %d, size=%r, container=%r)>' % (_class_name[self._tag_class], self._tag,
     49                                                                 self._size, self._container)
     50
     51 -    def __cmp__(self, other):
     52          if not isinstance(other, Tag):
     53              raise ValueError('not comparable with %r', other)
     54          x = cmp(self._tag_class, other._tag_class)
     55          if x != 0:
     56              return x
     57          x = cmp(self._container, other._container)
     58          if x != 0:
     59              return x
     60          return cmp(self._tag, other._tag)
     61
     62 -    def __hash__(self):
     63          n = self._tag_class
     64          n *= 37
     65          if self._container:
     66              n += 1
     67          n *= 37
     68          n += self._tag
     69          return n
     70
     71      tag_class = property(lambda self: self._tag_class, None, None)
     72      tag = property(lambda self: self._tag, None, None)
     73      size = property(lambda self: self._size, None, None)
     74      container = property(lambda self: self._container, None, None)
     75
     76 -    def is_terminator(self):
     77          """
     78          Return C{True} if this tag is the special type used to terminate
     79          indefinite-length sequences (type 0, length 0).
     80          """
     81          return (self._tag_class == UNIVERSAL) and not self._container and (self._tag == 0) and \
     82                 self._size == 0
     83
     84      @staticmethod
     85 -    def make_terminator():
     86          """
     87          Return a tag that can be used to terminate indefinite-length sequences.
     88          """
     89          return Tag(Tag.UNIVERSAL, False, 0, 0)
     90
     91      @staticmethod
     92 -    def from_tag(t, size=None):
     93          return Tag(t._tag_class, t._tag, size, t._container)
     94
     95      @staticmethod
     96 -    def from_stream(fd):
     97          tag = fd.read(1)
     98          if len(tag) == 0:
     99              # end of stream
    100              return None
    101          bytes_read = 1
    102          tag = ord(tag)
    103          tag_class = (tag >> 6)
    104          if tag & 0x20:
    105              tag_container = True
    106          else:
    107              tag_container = False
    108          tag &= 0x1f
    109          if tag == 0x1f:
    110              # extended form of tag
    111              tag = 0L
    112              while True:
    113                  t = fd.read(1)
    114                  if len(t) == 0:
    115                      raise BERException('abrupt end of tag field')
    116                  bytes_read += 1
    117                  t = ord(t)
    118                  tag = (tag << 7) | (t & 0x7f)
    119                  if not (t & 0x80):
    120                      break
    121          size = fd.read(1)
    122          if len(size) == 0:
    123              raise BERException('expected size field')
    124          bytes_read += 1
    125          size = ord(size)
    126          if size == 0x80:
    127              size = None
    128              if not tag_container:
    129                  raise BERException('indefinite-length tags must refer to sequences')
    130          elif size & 0x80:
    131              # extended size field
    132              size &= 0x7f
    133              x = fd.read(size)
    134              if len(x) != size:
    135                  raise BERException('truncated size field')
    136              bytes_read += size
    137              size = inflate_long(x, True)
    138          return Tag(tag_class, tag, size, container=tag_container, _bytes_read=bytes_read)
    139
    140 -    def __len__(self):
    141          if self._bytes_read > 0:
    142              return self._bytes_read
    143          n = 2
    144          if self.tag > 30:
    145              t = self.tag
    146              while t > 0:
    147                  n += 1
    148                  t >>= 7
    149          if self.size > 0x7f:
    150              n += len(deflate_long(self.size))
    151          return n
    152
    153 -    def write(self, fd):
    154          high_bits = (self._tag_class << 6)
    155          if self._container:
    156              high_bits |= 0x20
    157          if self._tag <= 30:
    158              fd.write(chr(self._tag | high_bits))
    159          elif self._tag <= 127:
    160              fd.write(chr(high_bits | 0x1f))
    161              fd.write(chr(self._tag))
    162          else:
    163              fd.write(chr(high_bits | 0x1f))
    164              tag = self._tag
    165              tagstr = chr(tag & 0x7f)
    166              tag >>= 7
    167              while tag > 0:
    168                  tagstr = chr(0x80 | (tag & 0x7f)) + tagstr
    169                  tag >>= 7
    170              fd.write(tagstr)
    171          # whew.  okay, now length.
    172          if self._size is None:
    173              fd.write(chr(0x80))
    174          elif self._size > 0x7f:
    175              lenstr = deflate_long(self._size)
    176              fd.write(chr(0x80 | len(lenstr)))
    177              fd.write(lenstr)
    178          else:
    179              fd.write(chr(self._size))
    180