Commit 9e32ec76 authored by Szilárd Pfeiffer's avatar Szilárd Pfeiffer
Browse files

Merge branch '21-ssh-handshake-messages'

Closes: #21
parents 2d9a53bb 3317afb1
Loading
Loading
Loading
Loading
Loading
+208 −4
Original line number Diff line number Diff line
@@ -204,6 +204,11 @@ class Authentication(AlgortihmOIDBase, enum.Enum):


class BlockCipher(enum.Enum):
    ACSS = BlockCipherParams(
        name='ACSS',
        key_size=40,
        block_size=None,
    )
    AES_128 = BlockCipherParams(
        name='AES_128',
        key_size=128,
@@ -234,6 +239,27 @@ class BlockCipher(enum.Enum):
        key_size=256,
        block_size=128,
    )
    BLOWFISH = BlockCipherParams(
        name='BLOWFISH',
        key_size=32,  # min
        # key_size_max=448,
        block_size=64,
    )
    TWOFISH128 = BlockCipherParams(
        name='TWOFISH',
        key_size=128,
        block_size=128,
    )
    TWOFISH192 = BlockCipherParams(
        name='TWOFISH',
        key_size=192,
        block_size=192,
    )
    TWOFISH256 = BlockCipherParams(
        name='TWOFISH',
        key_size=256,
        block_size=256,
    )
    CAMELLIA_128 = BlockCipherParams(
        name='CAMELLIA_128',
        key_size=128,
@@ -244,12 +270,29 @@ class BlockCipher(enum.Enum):
        key_size=256,
        block_size=128,
    )
    CAST_128 = BlockCipherParams(
        name='CAST_128',
        key_size=40,  # min
        # key_size_max=128,
        block_size=64,
    )
    CAST_256 = BlockCipherParams(
        name='CAST_256',
        key_size=128,  # min
        # key_size_max=256,
        block_size=128,
    )
    CHACHA20 = BlockCipherParams(
        name='CHACHA20',
        key_size=128,  # min
        # key_size_max=256,
        block_size=None,
    )
    CRYPTICORE = BlockCipherParams(  # Rabbit
        name='CryptiCore',
        key_size=128,
        block_size=None,
    )
    DES = BlockCipherParams(
        name='DES',
        key_size=56,
@@ -335,6 +378,11 @@ class BlockCipher(enum.Enum):
        key_size=128,
        block_size=None,
    )
    RC4_256 = BlockCipherParams(
        name='RC4_256',
        key_size=256,
        block_size=None,
    )
    SALSA20 = BlockCipherParams(
        name='Salsa20',
        key_size=256,
@@ -345,6 +393,21 @@ class BlockCipher(enum.Enum):
        key_size=128,
        block_size=128,
    )
    SERPENT_128 = BlockCipherParams(
        name='SERPENT_128',
        key_size=128,
        block_size=128,
    )
    SERPENT_192 = BlockCipherParams(
        name='SERPENT_192',
        key_size=192,
        block_size=128,
    )
    SERPENT_256 = BlockCipherParams(
        name='SERPENT_256',
        key_size=256,
        block_size=128,
    )
    TRIPLE_DES = BlockCipherParams(
        name='3DES',
        key_size=128,  # min
@@ -378,6 +441,9 @@ class BlockCipherMode(enum.Enum):
    CTR = BlockCipherModeParams(
        name='CTR',
    )
    ECB = BlockCipherModeParams(
        name='ECB',
    )
    EAX = BlockCipherModeParams(
        name='EAX',
    )
@@ -387,10 +453,13 @@ class BlockCipherMode(enum.Enum):
    MGM = BlockCipherModeParams(
        name='MGM',
    )
    OFB = BlockCipherModeParams(
        name='OFB',
    )


@attr.s(frozen=True)
class HashParams(AlgortihmOIDParams):
class HashParams(AlgortihmOIDOptionalParams):
    digest_size = attr.ib(attr.validators.instance_of(int))


@@ -425,6 +494,21 @@ class Hash(AlgortihmOIDBase, enum.Enum):
        oid='1.2.840.113549.2.5',
        digest_size=64
    )
    RIPEMD128 = HashParams(
        name='RIPEMD-128',
        oid='1.3.36.3.2.2',
        digest_size=128
    )
    RIPEMD160 = HashParams(
        name='RIPEMD-160',
        oid='1.3.36.3.2.1',
        digest_size=160
    )
    RIPEMD256 = HashParams(
        name='RIPEMD-256',
        oid='1.3.36.3.2.3',
        digest_size=256
    )
    SHA1 = HashParams(
        name='SHA1',
        oid='1.3.14.3.2.18',
@@ -490,6 +574,36 @@ class Hash(AlgortihmOIDBase, enum.Enum):
        oid='2.16.840.1.101.3.4.2.12',
        digest_size=256
    )
    TIGER_128 = HashParams(
        name='TIGER_128',
        oid=None,
        digest_size=128
    )
    TIGER_128_96 = HashParams(
        name='TIGER_128_96',
        oid=None,
        digest_size=96
    )
    TIGER_160 = HashParams(
        name='TIGER_160',
        oid=None,
        digest_size=160
    )
    TIGER_160_96 = HashParams(
        name='TIGER_160_96',
        oid=None,
        digest_size=96
    )
    TIGER_192 = HashParams(
        name='TIGER_192',
        oid='1.3.6.1.4.1.11591.12.2',
        digest_size=192
    )
    TIGER_192_96 = HashParams(
        name='TIGER_192_96',
        oid=None,
        digest_size=96
    )
    ED25519PH = HashParams(
        name='Ed25519ph',
        oid='1.3.101.114',
@@ -522,6 +636,31 @@ class HMACParams(MACParamsBase):


class MAC(AlgortihmOIDBase, enum.Enum):
    AEAD_AES_128_CCM = MACParams(
        name='AEAD_AES_128_CCM',
        oid=None,
        digest_size=128
    )
    AEAD_AES_128_GCM = MACParams(
        name='AEAD_AES_128_GCM',
        oid=None,
        digest_size=128
    )
    AEAD_AES_256_CCM = MACParams(
        name='AEAD_AES_256_CCM',
        oid=None,
        digest_size=256
    )
    AEAD_AES_256_GCM = MACParams(
        name='AEAD_AES_256_GCM',
        oid=None,
        digest_size=256
    )
    CRYPTICORE = MACParams(
        name='CryptiCore',  # Badger
        oid=None,
        digest_size=128
    )
    IMIT_GOST28147 = MACParams(
        name='IMIT_GOST28147',
        oid='1.2.643.2.2.22',
@@ -562,6 +701,21 @@ class MAC(AlgortihmOIDBase, enum.Enum):
        oid=None,
        digest_size=128
    )
    RIPEMD128 = HMACParams(
        name='RIPEMD-128',
        oid=None,
        hash_algo=Hash.RIPEMD128,
    )
    RIPEMD160 = HMACParams(
        name='RIPEMD-160',
        oid=None,
        hash_algo=Hash.RIPEMD160,
    )
    RIPEMD256 = HMACParams(
        name='RIPEMD-256',
        oid=None,
        hash_algo=Hash.RIPEMD256,
    )
    SHA1 = HMACParams(
        name='SHA1',
        oid='1.2.840.113549.2.7',
@@ -617,6 +771,56 @@ class MAC(AlgortihmOIDBase, enum.Enum):
        oid='2.16.840.1.101.3.4.2.16',
        hash_algo=Hash.SHA3_512
    )
    TIGER_128 = HMACParams(
        name='TIGER_128',
        oid=None,
        hash_algo=Hash.TIGER_128
    )
    TIGER_128_96 = HMACParams(
        name='TIGER_128_96',
        oid=None,
        hash_algo=Hash.TIGER_128_96
    )
    TIGER_160 = HMACParams(
        name='TIGER_160',
        oid=None,
        hash_algo=Hash.TIGER_160
    )
    TIGER_160_96 = HMACParams(
        name='TIGER_160_96',
        oid=None,
        hash_algo=Hash.TIGER_160_96
    )
    TIGER_192 = HMACParams(
        name='TIGER_192',
        oid='1.3.6.1.5.5.8.1.3',
        hash_algo=Hash.TIGER_192
    )
    TIGER_192_96 = HMACParams(
        name='TIGER_192_96',
        oid=None,
        hash_algo=Hash.TIGER_192_96
    )
    UMAC_32 = MACParams(
        name='UMAC_32',
        oid=None,
        digest_size=32
    )
    UMAC_64 = MACParams(
        name='UMAC_64',
        oid=None,
        digest_size=64
    )
    UMAC_96 = MACParams(
        name='UMAC_96',
        oid=None,
        digest_size=96
    )
    UMAC_128 = MACParams(
        name='UMAC_128',
        oid=None,
        digest_size=128
    )
    ED25519PH = HMACParams(
        name='Ed25519ph',
        oid='1.3.101.114',
+169 −40
Original line number Diff line number Diff line
@@ -13,10 +13,16 @@ except ImportError: # pragma: no cover
from collections import OrderedDict

import attr

import six

from cryptoparser.common.parse import ParsableBase, ParserBinary, ComposerBinary
from cryptoparser.common.parse import (
    ComposerBinary,
    ComposerText,
    ParsableBase,
    ParsableBaseNoABC,
    ParserBinary,
    ParserText,
)
from cryptoparser.common.exception import NotEnoughData, TooMuchData, InvalidValue, InvalidType


@@ -35,6 +41,17 @@ json.JSONEncoder.default = _default
class Serializable(object):  # pylint: disable=too-few-public-methods
    @staticmethod
    def _get_ordered_dict(dict_value):
        if attr.has(type(dict_value)):
            return OrderedDict([
                (name, getattr(dict_value, name))
                for name, field in attr.fields_dict(type(dict_value)).items()
            ])

        if isinstance(dict_value, dict):
            pass
        elif hasattr(dict_value, '__dict__'):
            dict_value = dict_value.__dict__

        result = OrderedDict([
            (name, dict_value[name])
            for name in sorted(dict_value.keys())
@@ -93,21 +110,20 @@ class Serializable(object): # pylint: disable=too-few-public-methods

        return name_dict

    def _markdown_result_dict(self, obj, level=0):
    @classmethod
    def _markdown_result_complex(cls, obj, level=0):
        indent = Serializable._markdown_indent_from_level(level)

        if isinstance(obj, dict):
            dict_value = Serializable._get_ordered_dict(obj)
        elif hasattr(obj, '_asdict'):
        if hasattr(obj, '_asdict'):
            dict_value = obj._asdict()
        if not isinstance(dict_value, dict):
            return False, dict_value
        else:
            dict_value = Serializable._get_ordered_dict(obj)

        result = ''
        name_dict = self._markdown_human_readable_names(obj, dict_value)
        name_dict = cls._markdown_human_readable_names(obj, dict_value)
        for name, value in dict_value.items():
            result += '{indent}* {name}'.format(indent=indent, name=name_dict[name])
            multiline, markdnow_result = self._markdown_result_simple(value, level)
            multiline, markdnow_result = cls._markdown_result(value, level + 1)
            if multiline:
                result += ':\n{result}'.format(result=markdnow_result)
            else:
@@ -118,61 +134,63 @@ class Serializable(object): # pylint: disable=too-few-public-methods

        return True, result

    @classmethod
    def _markdown_result_list(cls, obj, level=0):
        if not obj:
            return False, '-'

        indent = Serializable._markdown_indent_from_level(level)

        result = ''
        for index, item in enumerate(obj):
            multiline, markdnow_result = cls._markdown_result(item, level + 1)
            result += '{indent}{index}.{separator}{value}{newline}'.format(
                indent=indent,
                index=index + 1,
                separator='\n' if multiline else ' ',
                value=markdnow_result,
                newline='' if multiline else '\n',
            )

        return True, result

    @staticmethod
    def _markdown_is_directly_printable(obj):
        return isinstance(obj, six.string_types + six.integer_types + (float, ))

    def _markdown_result_simple(self, value, level):
        if isinstance(value, Serializable):
            return value._as_markdown(level + 1)  # pylint: disable=protected-access

        return self._markdown_result(value, level + 1)

    def _markdown_result(self, obj, level=0):
    @classmethod
    def _markdown_result(cls, obj, level=0):
        if obj is None:
            result = False, 'n/a'
        elif isinstance(obj, bool):
            result = False, 'yes' if obj else 'no'
        elif Serializable._markdown_is_directly_printable(obj):
            result = False, str(obj)
        elif isinstance(obj, Serializable):
            result = obj._as_markdown(level)  # pylint: disable=protected-access
        elif isinstance(obj, enum.Enum):
            if isinstance(obj.value, Serializable):
                return self._markdown_result_simple(obj.value, level)
                return obj.value._as_markdown(level)  # pylint: disable=protected-access

            return False, obj.name
        elif hasattr(obj, '__dict__') or isinstance(obj, dict):
            return self._markdown_result_dict(obj, level)
            result = cls._markdown_result_complex(obj, level)
        elif isinstance(obj, (list, tuple)):
            if obj:
                indent = Serializable._markdown_indent_from_level(level)

                result = ''
                for index, item in enumerate(obj):
                    multiline, markdnow_result = self._markdown_result_simple(item, level)
                    result += '{indent} {index}.{separator}{value}{newline}'.format(
                        indent=indent,
                        index=index + 1,
                        separator='\n' if multiline else ' ',
                        value=markdnow_result,
                        newline='' if multiline else '\n',
                    )
                result = True, result
            result = cls._markdown_result_list(obj, level)
        else:
                return False, '-'
        else:
            result = False, repr(obj)
            result = False, str(obj)

        return result

    def _asdict(self):
        result = Serializable._get_ordered_dict(self.__dict__)
        result = Serializable._get_ordered_dict(self)
        return result

    def as_json(self):
        return json.dumps(self)

    def _as_markdown(self, level):
        return self._markdown_result(self, level)
        return self._markdown_result_complex(self, level)

    def as_markdown(self):
        _, result = self._as_markdown(0)
@@ -274,6 +292,22 @@ class OpaqueParam(VectorParamNumeric): # pylint: disable=too-few-public-methods
        return 1


@attr.s
class VectorParamString(VectorParamBase):  # pylint: disable=too-few-public-methods
    separator = attr.ib(validator=attr.validators.instance_of(six.string_types), default=',')
    encoding = attr.ib(validator=attr.validators.instance_of(six.string_types), default='ascii')
    item_class = attr.ib(validator=attr.validators.instance_of(type), default=str)
    fallback_class = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(type)), default=None)

    def get_item_size(self, item):
        if isinstance(item, (ParsableBase, StringEnumParsable)):
            return len(item.compose())
        if isinstance(item, six.string_types):
            return len(item)

        raise NotImplementedError(type(item))


@attr.s
class VectorParamParsable(VectorParamBase):  # pylint: disable=too-few-public-methods
    item_class = attr.ib(validator=attr.validators.instance_of(type))
@@ -378,6 +412,45 @@ class Vector(VectorBase):
        return composer.composed_bytes


class VectorString(VectorBase):
    @classmethod
    @abc.abstractmethod
    def get_param(cls):
        raise NotImplementedError()

    @classmethod
    def _parse(cls, parsable):
        vector_param = cls.get_param()

        header_parser = ParserBinary(parsable[:vector_param.item_num_size])

        header_parser.parse_numeric('item_byte_num', vector_param.item_num_size)

        if header_parser['item_byte_num'] == 0:
            return cls([]), header_parser.parsed_length

        body_parser = ParserText(
            parsable[vector_param.item_num_size:header_parser['item_byte_num'] + vector_param.item_num_size]
        )

        body_parser.parse_string_array(
            'items', vector_param.separator, vector_param.item_class, vector_param.fallback_class,
        )

        return cls(body_parser['items']), header_parser.parsed_length + body_parser.parsed_length

    def compose(self):
        vector_param = self.get_param()

        body_composer = ComposerText(vector_param.encoding)
        body_composer.compose_parsable_array(self._items, vector_param.separator)

        header_composer = ComposerBinary()
        header_composer.compose_numeric(body_composer.composed_length, self.param.item_num_size)

        return header_composer.composed + body_composer.composed


class VectorParsable(VectorBase):
    @classmethod
    @abc.abstractmethod
@@ -442,6 +515,7 @@ class VectorParsableDerived(VectorBase):
        return header_composer.composed_bytes + body_composer.composed_bytes


@attr.s(init=False)
class Opaque(VectorBase):
    def __init__(self, items):
        if isinstance(items, (bytes, bytearray)):
@@ -454,7 +528,7 @@ class Opaque(VectorBase):
        parser = ParserBinary(parsable)

        parser.parse_numeric('item_byte_num', cls.get_param().item_num_size)
        parser.parse_bytes('items', parser['item_byte_num'])
        parser.parse_raw('items', parser['item_byte_num'])

        items = parser['items']
        return cls([ord(items[i:i + 1]) for i in range(len(items))]), parser.parsed_length
@@ -542,7 +616,7 @@ class NByteEnumComposer(enum.Enum):
            self.get_byte_num()
        )

        return composer.composed_bytes
        return composer.composed

    @classmethod
    @abc.abstractmethod
@@ -566,3 +640,58 @@ class ThreeByteEnumComposer(NByteEnumComposer):
    @classmethod
    def get_byte_num(cls):
        return 3


class StringEnumParsable(ParsableBaseNoABC):
    @classmethod
    def _parse(cls, parsable):
        enum_items = [
            enum_item
            for enum_item in cls  # pylint: disable=not-an-iterable
            if len(enum_item.value.code) <= len(parsable)
        ]
        enum_items.sort(key=lambda color: len(color.value.code), reverse=True)

        try:
            parsable.decode('ascii')
        except UnicodeDecodeError as e:
            six.raise_from(InvalidValue(parsable, cls), e)

        for enum_item in enum_items:
            if enum_item.value.code == parsable.decode('ascii'):
                return enum_item, len(enum_item.value.code)

        raise InvalidValue(parsable, cls, 'code')

    def compose(self):
        return self._asdict().encode('ascii')

    def _asdict(self):
        return getattr(self, 'value').code


@six.add_metaclass(abc.ABCMeta)
class ProtocolVersionBase(Serializable, ParsableBase):
    @classmethod
    @abc.abstractmethod
    def _parse(cls, parsable):
        raise NotImplementedError()

    @abc.abstractmethod
    def compose(self):
        raise NotImplementedError()

    @property
    @abc.abstractmethod
    def identifier(self):
        raise NotImplementedError()

    @abc.abstractmethod
    def __str__(self):
        raise NotImplementedError()

    def as_json(self):
        return json.dumps(self.identifier)

    def _as_markdown(self, level):
        return self._markdown_result(str(self), level)
+55 −0
Original line number Diff line number Diff line
#!/usr/bin/env python
# -*- coding: utf-8 -*-


from cryptoparser.common.exception import InvalidValue
from cryptoparser.common.parse import ParsableBase, ParserText, ComposerText


class LanguageTag(ParsableBase):
    def __init__(self, primary_subtag, subsequent_subtags=()):
        self._primary_subtag = None
        self._subsequent_subtags = None

        self.primary_subtag = primary_subtag
        self.subsequent_subtags = subsequent_subtags

    @property
    def primary_subtag(self):
        return self._primary_subtag

    @primary_subtag.setter
    def primary_subtag(self, value):
        if not value or len(value) > 8 or not value.isalpha():
            raise InvalidValue(value, LanguageTag, 'primary_subtag')

        self._primary_subtag = value

    @property
    def subsequent_subtags(self):
        return self._subsequent_subtags

    @subsequent_subtags.setter
    def subsequent_subtags(self, value):
        for subsequent_subtag in value:
            if not subsequent_subtag or len(subsequent_subtag) > 8 or not subsequent_subtag.isalnum():
                raise InvalidValue(value, LanguageTag, 'subsequent_subtag')

        self._subsequent_subtags = value

    @classmethod
    def _parse(cls, parsable):
        parser = ParserText(parsable)
        parser.parse_string_array('tags', '-')

        return LanguageTag(parser['tags'][0], parser['tags'][1:]), parser.parsed_length

    def compose(self):
        composer = ComposerText()

        composer.compose_string(self.primary_subtag)
        if self.subsequent_subtags:
            composer.compose_separator('-')
            composer.compose_string_array(self.subsequent_subtags, '-')

        return composer.composed
+5 −3
Original line number Diff line number Diff line
@@ -29,10 +29,12 @@ class InvalidValue(Exception):
        elif isinstance(value, int):
            message = hex(value)
        else:
            message = '{}'.format(value)
        message = '{} is not a valid {}'.format(message, type_class.__name__)
            message = value
        message = hex(value) if isinstance(value, int) else repr(value)
        type_name = type_class.__name__ if hasattr(type_class, '__name__') else str(type(type_class))
        message = u'{} is not a valid {}'.format(message, type_name)
        if class_member is not None:
            message = '{} {} value'.format(message, class_member)
            message = u'{} {} value'.format(message, class_member)

        super(InvalidValue, self).__init__(message)

+461 −37

File changed.

Preview size limit exceeded, changes collapsed.

Loading