Module hat.asn1

Abstract Syntax Notation One

Value Mapping

+-----------------------+------------------+
| ASN.1 type            | Python type      |
+=======================+==================+
| Boolean               | bool             |
+-----------------------+------------------+
| Integer               | int              |
+-----------------------+------------------+
| BitString             | List[bool]       |
+-----------------------+------------------+
| OctetString           | bytes            |
+-----------------------+------------------+
| Null                  | NoneType         |
+-----------------------+------------------+
| ObjectIdentifier      | List[int]        |
+-----------------------+------------------+
| String                | str              |
+-----------------------+------------------+
| External              | External         |
+-----------------------+------------------+
| Real                  | float            |
+-----------------------+------------------+
| Enumerated            | int              |
+-----------------------+------------------+
| EmbeddedPDV           | EmbeddedPDV      |
+-----------------------+------------------+
| Choice                | Tuple[str,Value] |
+-----------------------+------------------+
| Set                   | Dict[str,Value]  |
+-----------------------+------------------+
| SetOf                 | Iterable[Value]  |
+-----------------------+------------------+
| Sequence              | Dict[str,Value]  |
+-----------------------+------------------+
| SequenceOf            | List[Value]      |
+-----------------------+------------------+
| ABSTRACT-SYNTAX.&Type | Entity           |
+-----------------------+------------------+

For Choice, Set and Sequence, str represents field name.

Expand source code
"""Abstract Syntax Notation One

Value mapping
-------------

    +-----------------------+------------------+
    | ASN.1 type            | Python type      |
    +=======================+==================+
    | Boolean               | bool             |
    +-----------------------+------------------+
    | Integer               | int              |
    +-----------------------+------------------+
    | BitString             | List[bool]       |
    +-----------------------+------------------+
    | OctetString           | bytes            |
    +-----------------------+------------------+
    | Null                  | NoneType         |
    +-----------------------+------------------+
    | ObjectIdentifier      | List[int]        |
    +-----------------------+------------------+
    | String                | str              |
    +-----------------------+------------------+
    | External              | External         |
    +-----------------------+------------------+
    | Real                  | float            |
    +-----------------------+------------------+
    | Enumerated            | int              |
    +-----------------------+------------------+
    | EmbeddedPDV           | EmbeddedPDV      |
    +-----------------------+------------------+
    | Choice                | Tuple[str,Value] |
    +-----------------------+------------------+
    | Set                   | Dict[str,Value]  |
    +-----------------------+------------------+
    | SetOf                 | Iterable[Value]  |
    +-----------------------+------------------+
    | Sequence              | Dict[str,Value]  |
    +-----------------------+------------------+
    | SequenceOf            | List[Value]      |
    +-----------------------+------------------+
    | ABSTRACT-SYNTAX.&Type | Entity           |
    +-----------------------+------------------+

For Choice, Set and Sequence, `str` represents field name.

"""

import enum
import pathlib
import typing

from hat import json
from hat.asn1 import ber
from hat.asn1 import common
from hat.asn1 import doc
from hat.asn1.common import (Data,
                             Value,
                             Boolean,
                             Integer,
                             BitString,
                             OctetString,
                             Null,
                             ObjectIdentifier,
                             String,
                             External,
                             Real,
                             Enumerated,
                             EmbeddedPDV,
                             Choice,
                             Set,
                             SetOf,
                             Sequence,
                             SequenceOf,
                             Entity,
                             is_oid_eq)


__all__ = ['Data',
           'Value',
           'Boolean',
           'Integer',
           'BitString',
           'OctetString',
           'Null',
           'ObjectIdentifier',
           'String',
           'External',
           'Real',
           'Enumerated',
           'EmbeddedPDV',
           'Choice',
           'Set',
           'SetOf',
           'Sequence',
           'SequenceOf',
           'Entity',
           'is_oid_eq',
           'Encoding',
           'Encoder',
           'Repository']


Encoding = enum.Enum('Encoding', ['BER'])


class Repository:
    """ASN.1 type definition repository.

    Repository can be initialized with multiple arguments, which can be
    instances of ``pathlib.PurePath``, ``str`` or ``Repository``.

    If an argument is of type ``pathlib.PurePath``, and path points to file
    with a suffix '.asn', ASN.1 type definitions are decoded from the file.
    Otherwise, it is assumed that path points to a directory,
    which is recursively searched for ASN.1 definitions. All decoded types
    are added to the repository. Previously added type definitions with the
    same references are replaced.

    If an argument is of type ``str``, it represents ASN.1 type definitions.
    All decoded types are added to the repository. Previously added type
    definitions with the same references are replaced.

    If an argument is of type ``Repository``, its data definitions are added to
    the new repository. Previously added type definitions with the
    same references are replaced.

    """

    def __init__(self, *args: typing.Union[pathlib.PurePath,
                                           str,
                                           'Repository']):
        self._refs = {}
        for arg in args:
            if isinstance(arg, pathlib.PurePath):
                self._load_path(arg)
            elif isinstance(arg, str):
                self._parse_asn1_def(arg)
            elif isinstance(arg, Repository):
                self._refs.update(arg._refs)
            else:
                raise ValueError('invalid argument')

    @staticmethod
    def from_json(data: typing.Union[pathlib.PurePath, json.Data]
                  ) -> 'Repository':
        """Create repository from JSON data representation"""
        if isinstance(data, pathlib.PurePath):
            data = json.decode_file(data)
        repo = Repository()
        repo._refs = {common.type_from_json(k): common.type_from_json(v)
                      for k, v in data}
        return repo

    def to_json(self) -> json.Data:
        """Represent repository as JSON data"""
        return [[common.type_to_json(k), common.type_to_json(v)]
                for k, v in self._refs.items()]

    def generate_html_doc(self) -> str:
        """Generate HTML documentation"""
        return doc.generate_html(self._refs)

    def _load_path(self, path):
        paths = [path] if path.suffix == '.asn' else path.rglob('*.asn')
        for path in paths:
            with open(path, 'r', encoding='utf-8') as f:
                asn1_def = f.read()
            self._parse_asn1_def(asn1_def)

    def _parse_asn1_def(self, asn1_def):
        from hat.asn1 import parser
        refs = parser.parse(asn1_def)
        self._refs.update(refs)


class Encoder:
    """ASN1 Encoder"""

    def __init__(self,
                 encoding: Encoding,
                 repository: Repository):
        self._encoding = encoding
        self._repository = repository

    @property
    def syntax_name(self) -> ObjectIdentifier:
        """Encoder syntax name"""
        if self._encoding == Encoding.BER:
            return ber.syntax_name
        raise ValueError('invalid encoding')

    def encode(self,
               module: str,
               name: str,
               value: Value
               ) -> Data:
        """Encode value to data"""
        entity = self.encode_value(module, name, value)
        data = self.encode_entity(entity)
        return data

    def decode(self,
               module: str,
               name: str,
               data: Data
               ) -> typing.Tuple[Value, Data]:
        """Decode value from data

        Returns value and remaining data.

        """
        entity, rest = self.decode_entity(data)
        value = self.decode_value(module, name, entity)
        return value, rest

    def encode_value(self,
                     module: str,
                     name: str,
                     value: Value
                     ) -> Entity:
        """Encode value to entity"""
        if self._encoding == Encoding.BER:
            return ber.encode_value(self._repository._refs,
                                    common.TypeRef(module, name),
                                    value)
        raise ValueError('invalid encoding')

    def decode_value(self,
                     module: str,
                     name: str,
                     entity: Entity
                     ) -> Value:
        """Decode value from entity"""
        if self._encoding == Encoding.BER:
            return ber.decode_value(self._repository._refs,
                                    common.TypeRef(module, name),
                                    entity)
        raise ValueError('invalid encoding')

    def encode_entity(self,
                      entity: Entity
                      ) -> Data:
        """Encode entity to data"""
        if self._encoding == Encoding.BER:
            return ber.encode_entity(entity)
        raise ValueError('invalid encoding')

    def decode_entity(self,
                      data: Data
                      ) -> typing.Tuple[Entity, Data]:
        """Decode entity from data

        Returns entity and remaining data.

        """
        if self._encoding == Encoding.BER:
            return ber.decode_entity(data)
        raise ValueError('invalid encoding')

Sub-modules

hat.asn1.ber
hat.asn1.common
hat.asn1.doc
hat.asn1.parser

Functions

def is_oid_eq(x: ~ObjectIdentifier, y: ~ObjectIdentifier) ‑> bool

Check if two ASN.1 object identifiers are equal

Expand source code
def is_oid_eq(x: ObjectIdentifier,
              y: ObjectIdentifier
              ) -> bool:
    """Check if two ASN.1 object identifiers are equal"""
    if len(x) != len(y):
        return False
    for i, j in zip(x, y):
        i_id = i if isinstance(i, int) else i[1]
        j_id = j if isinstance(j, int) else j[1]
        if i_id != j_id:
            return False
    return True

Classes

class EmbeddedPDV (abstract: typing.Union[int, ~ObjectIdentifier, NoneType], transfer: typing.Optional[~ObjectIdentifier], data: ~Data)

EmbeddedPDV(abstract, transfer, data)

Expand source code
class EmbeddedPDV(typing.NamedTuple):
    abstract: typing.Optional[typing.Union[int, ObjectIdentifier]]
    transfer: typing.Optional[ObjectIdentifier]
    data: Data

Ancestors

  • builtins.tuple

Instance variables

var abstract : typing.Union[int, ~ObjectIdentifier, NoneType]

Alias for field number 0

var data : ~Data

Alias for field number 2

var transfer : typing.Optional[~ObjectIdentifier]

Alias for field number 1

class Encoder (encoding: Encoding, repository: Repository)

ASN1 Encoder

Expand source code
class Encoder:
    """ASN1 Encoder"""

    def __init__(self,
                 encoding: Encoding,
                 repository: Repository):
        self._encoding = encoding
        self._repository = repository

    @property
    def syntax_name(self) -> ObjectIdentifier:
        """Encoder syntax name"""
        if self._encoding == Encoding.BER:
            return ber.syntax_name
        raise ValueError('invalid encoding')

    def encode(self,
               module: str,
               name: str,
               value: Value
               ) -> Data:
        """Encode value to data"""
        entity = self.encode_value(module, name, value)
        data = self.encode_entity(entity)
        return data

    def decode(self,
               module: str,
               name: str,
               data: Data
               ) -> typing.Tuple[Value, Data]:
        """Decode value from data

        Returns value and remaining data.

        """
        entity, rest = self.decode_entity(data)
        value = self.decode_value(module, name, entity)
        return value, rest

    def encode_value(self,
                     module: str,
                     name: str,
                     value: Value
                     ) -> Entity:
        """Encode value to entity"""
        if self._encoding == Encoding.BER:
            return ber.encode_value(self._repository._refs,
                                    common.TypeRef(module, name),
                                    value)
        raise ValueError('invalid encoding')

    def decode_value(self,
                     module: str,
                     name: str,
                     entity: Entity
                     ) -> Value:
        """Decode value from entity"""
        if self._encoding == Encoding.BER:
            return ber.decode_value(self._repository._refs,
                                    common.TypeRef(module, name),
                                    entity)
        raise ValueError('invalid encoding')

    def encode_entity(self,
                      entity: Entity
                      ) -> Data:
        """Encode entity to data"""
        if self._encoding == Encoding.BER:
            return ber.encode_entity(entity)
        raise ValueError('invalid encoding')

    def decode_entity(self,
                      data: Data
                      ) -> typing.Tuple[Entity, Data]:
        """Decode entity from data

        Returns entity and remaining data.

        """
        if self._encoding == Encoding.BER:
            return ber.decode_entity(data)
        raise ValueError('invalid encoding')

Instance variables

var syntax_name : ~ObjectIdentifier

Encoder syntax name

Expand source code
@property
def syntax_name(self) -> ObjectIdentifier:
    """Encoder syntax name"""
    if self._encoding == Encoding.BER:
        return ber.syntax_name
    raise ValueError('invalid encoding')

Methods

def decode(self, module: str, name: str, data: ~Data) ‑> typing.Tuple[~Value, ~Data]

Decode value from data

Returns value and remaining data.

Expand source code
def decode(self,
           module: str,
           name: str,
           data: Data
           ) -> typing.Tuple[Value, Data]:
    """Decode value from data

    Returns value and remaining data.

    """
    entity, rest = self.decode_entity(data)
    value = self.decode_value(module, name, entity)
    return value, rest
def decode_entity(self, data: ~Data) ‑> typing.Tuple[Entity, ~Data]

Decode entity from data

Returns entity and remaining data.

Expand source code
def decode_entity(self,
                  data: Data
                  ) -> typing.Tuple[Entity, Data]:
    """Decode entity from data

    Returns entity and remaining data.

    """
    if self._encoding == Encoding.BER:
        return ber.decode_entity(data)
    raise ValueError('invalid encoding')
def decode_value(self, module: str, name: str, entity: Entity) ‑> ~Value

Decode value from entity

Expand source code
def decode_value(self,
                 module: str,
                 name: str,
                 entity: Entity
                 ) -> Value:
    """Decode value from entity"""
    if self._encoding == Encoding.BER:
        return ber.decode_value(self._repository._refs,
                                common.TypeRef(module, name),
                                entity)
    raise ValueError('invalid encoding')
def encode(self, module: str, name: str, value: ~Value) ‑> ~Data

Encode value to data

Expand source code
def encode(self,
           module: str,
           name: str,
           value: Value
           ) -> Data:
    """Encode value to data"""
    entity = self.encode_value(module, name, value)
    data = self.encode_entity(entity)
    return data
def encode_entity(self, entity: Entity) ‑> ~Data

Encode entity to data

Expand source code
def encode_entity(self,
                  entity: Entity
                  ) -> Data:
    """Encode entity to data"""
    if self._encoding == Encoding.BER:
        return ber.encode_entity(entity)
    raise ValueError('invalid encoding')
def encode_value(self, module: str, name: str, value: ~Value) ‑> Entity

Encode value to entity

Expand source code
def encode_value(self,
                 module: str,
                 name: str,
                 value: Value
                 ) -> Entity:
    """Encode value to entity"""
    if self._encoding == Encoding.BER:
        return ber.encode_value(self._repository._refs,
                                common.TypeRef(module, name),
                                value)
    raise ValueError('invalid encoding')
class Encoding (value, names=None, *, module=None, qualname=None, type=None, start=1)

An enumeration.

Ancestors

  • enum.Enum

Class variables

var BER
class Entity

Encoding independent ASN.1 Entity

Expand source code
class Entity(abc.ABC):
    """Encoding independent ASN.1 Entity"""

Ancestors

  • abc.ABC
class External (data: typing.Union[ForwardRef('Entity'), ~Data, typing.List[bool]], direct_ref: typing.Optional[~ObjectIdentifier], indirect_ref: typing.Optional[int])

External(data, direct_ref, indirect_ref)

Expand source code
class External(typing.NamedTuple):
    data: typing.Union['Entity', Data, typing.List[bool]]
    direct_ref: typing.Optional[ObjectIdentifier]
    indirect_ref: typing.Optional[int]

Ancestors

  • builtins.tuple

Instance variables

var data : typing.Union[Entity, ~Data, typing.List[bool]]

Alias for field number 0

var direct_ref : typing.Optional[~ObjectIdentifier]

Alias for field number 1

var indirect_ref : typing.Optional[int]

Alias for field number 2

class Repository (*args: typing.Union[pathlib.PurePath, str, ForwardRef('Repository')])

ASN.1 type definition repository.

Repository can be initialized with multiple arguments, which can be instances of pathlib.PurePath, str or Repository.

If an argument is of type pathlib.PurePath, and path points to file with a suffix '.asn', ASN.1 type definitions are decoded from the file. Otherwise, it is assumed that path points to a directory, which is recursively searched for ASN.1 definitions. All decoded types are added to the repository. Previously added type definitions with the same references are replaced.

If an argument is of type str, it represents ASN.1 type definitions. All decoded types are added to the repository. Previously added type definitions with the same references are replaced.

If an argument is of type Repository, its data definitions are added to the new repository. Previously added type definitions with the same references are replaced.

Expand source code
class Repository:
    """ASN.1 type definition repository.

    Repository can be initialized with multiple arguments, which can be
    instances of ``pathlib.PurePath``, ``str`` or ``Repository``.

    If an argument is of type ``pathlib.PurePath``, and path points to file
    with a suffix '.asn', ASN.1 type definitions are decoded from the file.
    Otherwise, it is assumed that path points to a directory,
    which is recursively searched for ASN.1 definitions. All decoded types
    are added to the repository. Previously added type definitions with the
    same references are replaced.

    If an argument is of type ``str``, it represents ASN.1 type definitions.
    All decoded types are added to the repository. Previously added type
    definitions with the same references are replaced.

    If an argument is of type ``Repository``, its data definitions are added to
    the new repository. Previously added type definitions with the
    same references are replaced.

    """

    def __init__(self, *args: typing.Union[pathlib.PurePath,
                                           str,
                                           'Repository']):
        self._refs = {}
        for arg in args:
            if isinstance(arg, pathlib.PurePath):
                self._load_path(arg)
            elif isinstance(arg, str):
                self._parse_asn1_def(arg)
            elif isinstance(arg, Repository):
                self._refs.update(arg._refs)
            else:
                raise ValueError('invalid argument')

    @staticmethod
    def from_json(data: typing.Union[pathlib.PurePath, json.Data]
                  ) -> 'Repository':
        """Create repository from JSON data representation"""
        if isinstance(data, pathlib.PurePath):
            data = json.decode_file(data)
        repo = Repository()
        repo._refs = {common.type_from_json(k): common.type_from_json(v)
                      for k, v in data}
        return repo

    def to_json(self) -> json.Data:
        """Represent repository as JSON data"""
        return [[common.type_to_json(k), common.type_to_json(v)]
                for k, v in self._refs.items()]

    def generate_html_doc(self) -> str:
        """Generate HTML documentation"""
        return doc.generate_html(self._refs)

    def _load_path(self, path):
        paths = [path] if path.suffix == '.asn' else path.rglob('*.asn')
        for path in paths:
            with open(path, 'r', encoding='utf-8') as f:
                asn1_def = f.read()
            self._parse_asn1_def(asn1_def)

    def _parse_asn1_def(self, asn1_def):
        from hat.asn1 import parser
        refs = parser.parse(asn1_def)
        self._refs.update(refs)

Static methods

def from_json(data: typing.Union[pathlib.PurePath, ~Data]) ‑> Repository

Create repository from JSON data representation

Expand source code
@staticmethod
def from_json(data: typing.Union[pathlib.PurePath, json.Data]
              ) -> 'Repository':
    """Create repository from JSON data representation"""
    if isinstance(data, pathlib.PurePath):
        data = json.decode_file(data)
    repo = Repository()
    repo._refs = {common.type_from_json(k): common.type_from_json(v)
                  for k, v in data}
    return repo

Methods

def generate_html_doc(self) ‑> str

Generate HTML documentation

Expand source code
def generate_html_doc(self) -> str:
    """Generate HTML documentation"""
    return doc.generate_html(self._refs)
def to_json(self) ‑> ~Data

Represent repository as JSON data

Expand source code
def to_json(self) -> json.Data:
    """Represent repository as JSON data"""
    return [[common.type_to_json(k), common.type_to_json(v)]
            for k, v in self._refs.items()]