cryptoparser 0.10.2__tar.gz → 0.10.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CHANGELOG.rst +10 -0
  2. {CryptoParser-0.10.2 → CryptoParser-0.10.3/CryptoParser.egg-info}/PKG-INFO +3 -3
  3. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CryptoParser.egg-info/SOURCES.txt +3 -0
  4. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CryptoParser.egg-info/requires.txt +1 -1
  5. {CryptoParser-0.10.2/CryptoParser.egg-info → CryptoParser-0.10.3}/PKG-INFO +3 -3
  6. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/__setup__.py +1 -1
  7. CryptoParser-0.10.3/cryptoparser/dnsrec/record.py +478 -0
  8. CryptoParser-0.10.3/cryptoparser/dnsrec/txt.py +911 -0
  9. CryptoParser-0.10.3/cryptoparser/tls/__init__.py +0 -0
  10. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/requirements.txt +1 -1
  11. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/setup.py +2 -1
  12. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CONTRIBUTING.rst +0 -0
  13. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CryptoParser.egg-info/dependency_links.txt +0 -0
  14. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/CryptoParser.egg-info/top_level.txt +0 -0
  15. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/LICENSE.txt +0 -0
  16. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/MANIFEST.in +0 -0
  17. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/README.rst +0 -0
  18. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/__init__.py +0 -0
  19. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/__init__.py +0 -0
  20. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/base.py +0 -0
  21. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/classes.py +0 -0
  22. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/exception.py +0 -0
  23. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/field.py +0 -0
  24. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/parse.py +0 -0
  25. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/utils.py +0 -0
  26. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/common/x509.py +0 -0
  27. {CryptoParser-0.10.2/cryptoparser/httpx → CryptoParser-0.10.3/cryptoparser/dnsrec}/__init__.py +0 -0
  28. {CryptoParser-0.10.2/cryptoparser/ssh → CryptoParser-0.10.3/cryptoparser/httpx}/__init__.py +0 -0
  29. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/httpx/header.py +0 -0
  30. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/httpx/parse.py +0 -0
  31. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/httpx/version.py +0 -0
  32. {CryptoParser-0.10.2/cryptoparser/tls → CryptoParser-0.10.3/cryptoparser/ssh}/__init__.py +0 -0
  33. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/ssh/key.py +0 -0
  34. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/ssh/record.py +0 -0
  35. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/ssh/subprotocol.py +0 -0
  36. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/ssh/version.py +0 -0
  37. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/algorithm.py +0 -0
  38. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/ciphersuite.py +0 -0
  39. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/extension.py +0 -0
  40. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/grease.py +0 -0
  41. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/ldap.py +0 -0
  42. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/mysql.py +0 -0
  43. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/openvpn.py +0 -0
  44. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/postgresql.py +0 -0
  45. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/rdp.py +0 -0
  46. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/record.py +0 -0
  47. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/subprotocol.py +0 -0
  48. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/cryptoparser/tls/version.py +0 -0
  49. {CryptoParser-0.10.2 → CryptoParser-0.10.3}/setup.cfg +0 -0
@@ -1,6 +1,16 @@
1
1
  Changelog
2
2
  =========
3
3
 
4
+ 0.10.3 - 2023-10-12
5
+ -------------------
6
+
7
+ Notable fixes
8
+ ~~~~~~~~~~~~~
9
+
10
+ - Generic
11
+
12
+ - add missing dnsrec module to the packaging (#75)
13
+
4
14
  0.10.2 - 2023-08-28
5
15
  -------------------
6
16
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: CryptoParser
3
- Version: 0.10.2
3
+ Version: 0.10.3
4
4
  Summary: Fast and flexible security protocol parser and generator
5
5
  Author: Szilárd Pfeiffer
6
6
  Author-email: coroner@pfeifferszilard.hu
@@ -12,7 +12,7 @@ Project-URL: Changelog, https://cryptoparser.readthedocs.io/en/latest/changelog
12
12
  Project-URL: Documentation, https://cryptoparser.readthedocs.io/en/latest/
13
13
  Project-URL: Issues, https://gitlab.com/coroner/cryptoparser/-/issues
14
14
  Project-URL: Source, https://gitlab.com/coroner/cryptoparser
15
- Keywords: ssl tls gost ja3 ldap rdp ssh hsts
15
+ Keywords: ssl tls gost ja3 ldap rdp ssh hsts dns
16
16
  Classifier: Development Status :: 4 - Beta
17
17
  Classifier: Environment :: Console
18
18
  Classifier: Framework :: tox
@@ -48,7 +48,7 @@ Description-Content-Type: text/x-rst
48
48
  License-File: LICENSE.txt
49
49
  Requires-Dist: asn1crypto
50
50
  Requires-Dist: attrs<22.0.1,>=20.3.0
51
- Requires-Dist: cryptodatahub>=0.10.0
51
+ Requires-Dist: cryptodatahub>=0.10.3
52
52
  Requires-Dist: python-dateutil
53
53
  Requires-Dist: py2-ipaddress; python_version < "3.4"
54
54
  Requires-Dist: typing; python_version < "3.5"
@@ -20,6 +20,9 @@ cryptoparser/common/field.py
20
20
  cryptoparser/common/parse.py
21
21
  cryptoparser/common/utils.py
22
22
  cryptoparser/common/x509.py
23
+ cryptoparser/dnsrec/__init__.py
24
+ cryptoparser/dnsrec/record.py
25
+ cryptoparser/dnsrec/txt.py
23
26
  cryptoparser/httpx/__init__.py
24
27
  cryptoparser/httpx/header.py
25
28
  cryptoparser/httpx/parse.py
@@ -1,6 +1,6 @@
1
1
  asn1crypto
2
2
  attrs<22.0.1,>=20.3.0
3
- cryptodatahub>=0.10.0
3
+ cryptodatahub>=0.10.3
4
4
  python-dateutil
5
5
  python-dateutil
6
6
  urllib3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: CryptoParser
3
- Version: 0.10.2
3
+ Version: 0.10.3
4
4
  Summary: Fast and flexible security protocol parser and generator
5
5
  Author: Szilárd Pfeiffer
6
6
  Author-email: coroner@pfeifferszilard.hu
@@ -12,7 +12,7 @@ Project-URL: Changelog, https://cryptoparser.readthedocs.io/en/latest/changelog
12
12
  Project-URL: Documentation, https://cryptoparser.readthedocs.io/en/latest/
13
13
  Project-URL: Issues, https://gitlab.com/coroner/cryptoparser/-/issues
14
14
  Project-URL: Source, https://gitlab.com/coroner/cryptoparser
15
- Keywords: ssl tls gost ja3 ldap rdp ssh hsts
15
+ Keywords: ssl tls gost ja3 ldap rdp ssh hsts dns
16
16
  Classifier: Development Status :: 4 - Beta
17
17
  Classifier: Environment :: Console
18
18
  Classifier: Framework :: tox
@@ -48,7 +48,7 @@ Description-Content-Type: text/x-rst
48
48
  License-File: LICENSE.txt
49
49
  Requires-Dist: asn1crypto
50
50
  Requires-Dist: attrs<22.0.1,>=20.3.0
51
- Requires-Dist: cryptodatahub>=0.10.0
51
+ Requires-Dist: cryptodatahub>=0.10.3
52
52
  Requires-Dist: python-dateutil
53
53
  Requires-Dist: py2-ipaddress; python_version < "3.4"
54
54
  Requires-Dist: typing; python_version < "3.5"
@@ -2,7 +2,7 @@
2
2
 
3
3
  __title__ = 'CryptoParser'
4
4
  __technical_name__ = __title__.lower()
5
- __version__ = '0.10.2'
5
+ __version__ = '0.10.3'
6
6
  __description__ = 'Fast and flexible security protocol parser and generator'
7
7
  __author__ = 'Szilárd Pfeiffer'
8
8
  __author_email__ = 'coroner@pfeifferszilard.hu'
@@ -0,0 +1,478 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ import abc
4
+ import collections
5
+ import datetime
6
+ import enum
7
+
8
+ import attr
9
+ import six
10
+
11
+ from cryptodatahub.common.algorithm import Authentication, NamedGroup, Signature
12
+ from cryptodatahub.common.exception import InvalidValue
13
+ from cryptodatahub.common.key import (
14
+ PublicKey,
15
+ PublicKeyParamsDsa,
16
+ PublicKeyParamsEcdsa,
17
+ PublicKeyParamsEddsa,
18
+ PublicKeyParamsRsa,
19
+ )
20
+
21
+ from cryptodatahub.dnsrec.algorithm import DnsRrType, DnsSecAlgorithm, DnsSecDigestType
22
+
23
+ from cryptoparser.common.base import OneByteEnumParsable, Serializable, TwoByteEnumParsable
24
+ from cryptoparser.common.exception import NotEnoughData
25
+ from cryptoparser.common.parse import ByteOrder, ComposerBinary, ParsableBase, ParserBinary
26
+
27
+
28
+ class DnsSecProtocol(enum.Enum):
29
+ V3 = 3
30
+
31
+
32
+ class DnsSecFlag(enum.IntEnum):
33
+ SECURE_ENTRY_POINT = 0x0001
34
+ REVOKE = 0x0080
35
+ DNS_ZONE_KEY = 0x0100
36
+
37
+
38
+ class DnsSecAlgorithmFactory(OneByteEnumParsable):
39
+ @classmethod
40
+ def get_enum_class(cls):
41
+ return DnsSecAlgorithm
42
+
43
+ @abc.abstractmethod
44
+ def compose(self):
45
+ raise NotImplementedError()
46
+
47
+
48
+ @attr.s
49
+ class DnsRecordDnskey(ParsableBase, Serializable):
50
+ HEADER_SIZE = 4
51
+
52
+ flags = attr.ib(validator=attr.validators.deep_iterable(attr.validators.instance_of(DnsSecFlag)))
53
+ algorithm = attr.ib(validator=attr.validators.instance_of(DnsSecAlgorithm))
54
+ key = attr.ib(validator=attr.validators.instance_of(PublicKey))
55
+ protocol = attr.ib(validator=attr.validators.instance_of(DnsSecProtocol))
56
+
57
+ def __attrs_post_init__(self):
58
+ if not isinstance(self.algorithm.value.algorithm, Signature):
59
+ raise InvalidValue(self.algorithm.value.algorithm, type(self), 'algorithm_type')
60
+
61
+ algorithm_key_type = self.algorithm.value.algorithm.value.key_type
62
+ algorithm_incompatible = self.key.key_type != algorithm_key_type
63
+ algorithm_compatible_gost = (
64
+ self.algorithm == DnsSecAlgorithm.ECCGOST and
65
+ self.key.key_type == Authentication.ECDSA and
66
+ self.key.params.named_group == NamedGroup.GC256B
67
+ )
68
+ if (algorithm_incompatible and not algorithm_compatible_gost):
69
+ raise InvalidValue(algorithm_key_type, type(self), 'key_type')
70
+
71
+ @property
72
+ def key_tag(self):
73
+ if self.algorithm == DnsSecAlgorithm.RSAMD5:
74
+ return (self.key.params.modulus & 0xffffff) >> 8
75
+
76
+ key_tag = 0
77
+ parser = ParserBinary(self.compose(), byte_order=ByteOrder.BIG_ENDIAN)
78
+
79
+ while parser.unparsed_length > 1:
80
+ parser.parse_numeric('value', 2)
81
+ key_tag += parser['value']
82
+ if parser.unparsed_length:
83
+ parser.parse_numeric('value', 1)
84
+ key_tag += parser['value']
85
+
86
+ key_tag += (key_tag >> 16) & 0xffff
87
+ return key_tag & 0xffff
88
+
89
+ def _asdict(self):
90
+ dict_value = super(DnsRecordDnskey, self)._asdict()
91
+ return collections.OrderedDict([('key_tag', self.key_tag)] + list(dict_value.items()))
92
+
93
+ @classmethod
94
+ def _parse_public_key_rsa(cls, key_parser):
95
+ key_parser.parse_numeric('exponent_length_one_octet', 1)
96
+ exponent_length = key_parser['exponent_length_one_octet']
97
+ if exponent_length == 0:
98
+ key_parser.parse_numeric('exponent_length_two_octets', 2)
99
+ exponent_length = key_parser['exponent_length_two_octets']
100
+ key_parser.parse_mpint('public_exponent', exponent_length)
101
+ key_parser.parse_mpint('modulus', key_parser.unparsed_length)
102
+
103
+ return PublicKey.from_params(PublicKeyParamsRsa(
104
+ public_exponent=key_parser['public_exponent'],
105
+ modulus=key_parser['modulus'],
106
+ ))
107
+
108
+ @classmethod
109
+ def _parse_public_key_ecdsa(cls, dnssec_algorithm, key_parser):
110
+ if dnssec_algorithm == DnsSecAlgorithm.ECDSAP256SHA256:
111
+ named_group = NamedGroup.SECP256K1
112
+ elif dnssec_algorithm == DnsSecAlgorithm.ECDSAP384SHA384:
113
+ named_group = NamedGroup.SECP384R1
114
+ elif dnssec_algorithm == DnsSecAlgorithm.ECCGOST:
115
+ named_group = NamedGroup.GC256B
116
+ else:
117
+ raise NotImplementedError(dnssec_algorithm)
118
+
119
+ key_size = named_group.value.size // 8
120
+ key_parser.parse_mpint('x', key_size)
121
+ key_parser.parse_mpint('y', key_size)
122
+
123
+ return PublicKey.from_params(PublicKeyParamsEcdsa(
124
+ point_x=key_parser['x'], point_y=key_parser['y'], named_group=named_group,
125
+ ))
126
+
127
+ @classmethod
128
+ def _parse_public_key_eddsa(cls, public_key_type, key_parser):
129
+ if public_key_type == Authentication.ED25519:
130
+ key_parser.parse_raw('public_key', 256 // 8)
131
+ elif public_key_type == Authentication.ED448:
132
+ key_parser.parse_raw('public_key', 448 // 8)
133
+ else:
134
+ raise NotImplementedError(public_key_type)
135
+
136
+ return PublicKey.from_params(PublicKeyParamsEddsa(
137
+ key_data=key_parser['public_key'], key_type=public_key_type,
138
+ ))
139
+
140
+ @classmethod
141
+ def _parse_public_key_dss(cls, key_parser):
142
+ key_parser.parse_numeric('t', 1)
143
+ key_parser.parse_mpint('q', 20)
144
+
145
+ mpint_length = 64 + key_parser['t'] * 8
146
+ key_parser.parse_mpint('p', mpint_length)
147
+ key_parser.parse_mpint('g', mpint_length)
148
+ key_parser.parse_mpint('y', mpint_length)
149
+
150
+ return PublicKey.from_params(PublicKeyParamsDsa(
151
+ prime=key_parser['p'],
152
+ generator=key_parser['g'],
153
+ order=key_parser['q'],
154
+ public_key_value=key_parser['y'],
155
+ ))
156
+
157
+ @classmethod
158
+ def parse_key(cls, parsable, dnssec_algorithm):
159
+ key_parser = ParserBinary(parsable)
160
+
161
+ public_key_type = dnssec_algorithm.value.algorithm.value.key_type
162
+ if public_key_type == Authentication.RSA:
163
+ public_key = cls._parse_public_key_rsa(key_parser)
164
+ elif public_key_type in [Authentication.ECDSA, Authentication.GOST_R3410_01]:
165
+ public_key = cls._parse_public_key_ecdsa(dnssec_algorithm, key_parser)
166
+ elif public_key_type in [Authentication.ED25519, Authentication.ED448]:
167
+ public_key = cls._parse_public_key_eddsa(public_key_type, key_parser)
168
+ elif public_key_type == Authentication.DSS:
169
+ public_key = cls._parse_public_key_dss(key_parser)
170
+ else:
171
+ raise NotImplementedError(dnssec_algorithm)
172
+
173
+ return public_key
174
+
175
+ @classmethod
176
+ def _parse(cls, parsable):
177
+ if len(parsable) < cls.HEADER_SIZE:
178
+ raise NotEnoughData(cls.HEADER_SIZE - len(parsable))
179
+
180
+ parser = ParserBinary(parsable)
181
+
182
+ parser.parse_numeric_flags('flags', 2, DnsSecFlag)
183
+ parser.parse_numeric('protocol', 1, DnsSecProtocol)
184
+
185
+ parser.parse_parsable('algorithm', DnsSecAlgorithmFactory)
186
+ parser.parse_raw('key', parser.unparsed_length)
187
+
188
+ public_key = cls.parse_key(parser['key'], parser['algorithm'])
189
+
190
+ return cls(
191
+ parser['flags'],
192
+ parser['algorithm'],
193
+ public_key,
194
+ parser['protocol'],
195
+ ), parser.parsed_length
196
+
197
+ @staticmethod
198
+ def _compose_public_key_rsa(key_composer, key):
199
+ key_params = key.params
200
+ exponent_length = (key_params.public_exponent.bit_length() + 7) // 8
201
+ if exponent_length > 255:
202
+ key_composer.compose_numeric(0, 1)
203
+ key_composer.compose_numeric(exponent_length, 2)
204
+ else:
205
+ key_composer.compose_numeric(exponent_length, 1)
206
+
207
+ key_composer.compose_mpint(key_params.public_exponent, exponent_length)
208
+ key_composer.compose_mpint(key_params.modulus, key.key_size // 8)
209
+
210
+ @staticmethod
211
+ def _compose_public_key_ecdsa(key_composer, key):
212
+ key_params = key.params
213
+ key_size = key.key_size // 8
214
+ key_composer.compose_mpint(key_params.point_x, key_size)
215
+ key_composer.compose_mpint(key_params.point_y, key_size)
216
+
217
+ @staticmethod
218
+ def _compose_public_key_eddsa(key_composer, key):
219
+ key_params = key.params
220
+ key_composer.compose_raw(key_params.key_data)
221
+
222
+ @staticmethod
223
+ def _compose_public_key_dss(key_composer, key):
224
+ key_params = key.params
225
+ key_size = key.key_size // 8
226
+
227
+ key_composer.compose_numeric((key_size - 64) // 8, 1)
228
+ key_composer.compose_mpint(key_params.order, 20)
229
+
230
+ key_composer.compose_mpint(key_params.prime, key_size)
231
+ key_composer.compose_mpint(key_params.generator, key_size)
232
+ key_composer.compose_mpint(key_params.public_key_value, key_size)
233
+
234
+ @staticmethod
235
+ def compose_key(key):
236
+ key_composer = ComposerBinary()
237
+ public_key_type = key.key_type
238
+
239
+ if public_key_type == Authentication.RSA:
240
+ DnsRecordDnskey._compose_public_key_rsa(key_composer, key)
241
+ elif public_key_type in [Authentication.ECDSA, Authentication.GOST_R3410_01]:
242
+ DnsRecordDnskey._compose_public_key_ecdsa(key_composer, key)
243
+ elif public_key_type in [Authentication.ED25519, Authentication.ED448]:
244
+ DnsRecordDnskey._compose_public_key_eddsa(key_composer, key)
245
+ elif public_key_type == Authentication.DSS:
246
+ DnsRecordDnskey._compose_public_key_dss(key_composer, key)
247
+ else:
248
+ raise NotImplementedError(public_key_type)
249
+
250
+ return key_composer.composed_bytes
251
+
252
+ def compose(self):
253
+ composer = ComposerBinary()
254
+
255
+ composer.compose_numeric_flags(self.flags, 2)
256
+ composer.compose_numeric(self.protocol.value, 1)
257
+
258
+ composer.compose_numeric_enum_coded(self.algorithm)
259
+
260
+ key_bytes = self.compose_key(self.key)
261
+
262
+ return composer.composed_bytes + key_bytes
263
+
264
+
265
+ class DnsSecDigestTypeFactory(OneByteEnumParsable):
266
+ @classmethod
267
+ def get_enum_class(cls):
268
+ return DnsSecDigestType
269
+
270
+ @abc.abstractmethod
271
+ def compose(self):
272
+ raise NotImplementedError()
273
+
274
+
275
+ @attr.s
276
+ class DnsRecordDs(ParsableBase):
277
+ HEADER_SIZE = 4
278
+
279
+ key_tag = attr.ib(validator=attr.validators.instance_of(six.integer_types))
280
+ algorithm = attr.ib(validator=attr.validators.instance_of(DnsSecAlgorithm))
281
+ digest_type = attr.ib(validator=attr.validators.instance_of(DnsSecDigestType))
282
+ digest = attr.ib(validator=attr.validators.instance_of((bytes, bytearray)))
283
+
284
+ @classmethod
285
+ def _parse(cls, parsable):
286
+ if len(parsable) < cls.HEADER_SIZE:
287
+ raise NotEnoughData(cls.HEADER_SIZE - len(parsable))
288
+
289
+ parser = ParserBinary(parsable)
290
+
291
+ parser.parse_numeric('key_tag', 2)
292
+ parser.parse_parsable('algorithm', DnsSecAlgorithmFactory)
293
+ parser.parse_parsable('digest_type', DnsSecDigestTypeFactory)
294
+ parser.parse_raw('digest', parser.unparsed_length)
295
+
296
+ return cls(**parser), parser.parsed_length
297
+
298
+ def compose(self):
299
+ composer = ComposerBinary()
300
+
301
+ composer.compose_numeric(self.key_tag, 2)
302
+ composer.compose_numeric_enum_coded(self.algorithm)
303
+ composer.compose_numeric_enum_coded(self.digest_type)
304
+ composer.compose_raw(self.digest)
305
+
306
+ return composer.composed_bytes
307
+
308
+
309
+ class DnsRrTypeFactory(TwoByteEnumParsable):
310
+ @classmethod
311
+ def get_enum_class(cls):
312
+ return DnsRrType
313
+
314
+ @abc.abstractmethod
315
+ def compose(self):
316
+ raise NotImplementedError()
317
+
318
+
319
+ @attr.s
320
+ class DnsNameUncompressed(ParsableBase, Serializable):
321
+ labels = attr.ib(
322
+ validator=attr.validators.deep_iterable(member_validator=attr.validators.instance_of(six.string_types))
323
+ )
324
+
325
+ def __str__(self):
326
+ return six.u('.').join(self.labels)
327
+
328
+ def _as_markdown(self, level):
329
+ return self._markdown_result(str(self), level)
330
+
331
+ @classmethod
332
+ def convert(cls, value):
333
+ if isinstance(value, cls):
334
+ return value
335
+ if isinstance(value, six.string_types):
336
+ if not value:
337
+ return cls([])
338
+
339
+ return cls(value.split('.'))
340
+
341
+ raise InvalidValue(value, cls, 'labels')
342
+
343
+ @classmethod
344
+ def _parse(cls, parsable):
345
+ parser = ParserBinary(parsable)
346
+
347
+ labels = []
348
+ while True:
349
+ parser.parse_string('label', 1, encoding='idna')
350
+ label = parser['label']
351
+
352
+ if not label:
353
+ break
354
+
355
+ labels.append(label)
356
+
357
+ return cls(labels), parser.parsed_length
358
+
359
+ def compose(self):
360
+ composer = ComposerBinary()
361
+
362
+ for label in self.labels:
363
+ composer.compose_string(label, 'idna', 1)
364
+
365
+ composer.compose_numeric(0, 1)
366
+
367
+ return composer.composed_bytes
368
+
369
+
370
+ @attr.s
371
+ class DnsRecordRrsig(ParsableBase): # pylint: disable=too-many-instance-attributes
372
+ HEADER_SIZE = 24
373
+
374
+ type_covered = attr.ib(validator=attr.validators.instance_of(DnsRrType))
375
+ algorithm = attr.ib(validator=attr.validators.instance_of(DnsSecAlgorithm))
376
+ labels = attr.ib(validator=attr.validators.instance_of(six.integer_types))
377
+ original_ttl = attr.ib(
378
+ validator=attr.validators.instance_of(six.integer_types),
379
+ metadata={'human_readable_name': 'Original TTL'}
380
+ )
381
+ signature_expiration = attr.ib(validator=attr.validators.instance_of(datetime.datetime))
382
+ signature_inception = attr.ib(validator=attr.validators.instance_of(datetime.datetime))
383
+ key_tag = attr.ib(validator=attr.validators.instance_of(six.integer_types))
384
+ signers_name = attr.ib(
385
+ converter=DnsNameUncompressed.convert,
386
+ validator=attr.validators.instance_of(DnsNameUncompressed)
387
+ )
388
+ signature = attr.ib(validator=attr.validators.instance_of((bytes, bytearray)))
389
+
390
+ @classmethod
391
+ def _parse(cls, parsable):
392
+ if len(parsable) < cls.HEADER_SIZE:
393
+ raise NotEnoughData(cls.HEADER_SIZE - len(parsable))
394
+
395
+ parser = ParserBinary(parsable)
396
+
397
+ parser.parse_parsable('type_covered', DnsRrTypeFactory)
398
+ parser.parse_parsable('algorithm', DnsSecAlgorithmFactory)
399
+ parser.parse_numeric('labels', 1)
400
+ parser.parse_numeric('original_ttl', 4)
401
+ parser.parse_timestamp('signature_expiration', item_size=4)
402
+ parser.parse_timestamp('signature_inception', item_size=4)
403
+ parser.parse_numeric('key_tag', 2)
404
+ parser.parse_parsable('signers_name', DnsNameUncompressed)
405
+ parser.parse_raw('signature', parser.unparsed_length)
406
+
407
+ return cls(**parser), parser.parsed_length
408
+
409
+ def compose(self):
410
+ composer = ComposerBinary()
411
+
412
+ composer.compose_numeric_enum_coded(self.type_covered)
413
+ composer.compose_numeric_enum_coded(self.algorithm)
414
+ composer.compose_numeric(self.labels, 1)
415
+ composer.compose_numeric(self.original_ttl, 4)
416
+ composer.compose_timestamp(self.signature_expiration, item_size=4)
417
+ composer.compose_timestamp(self.signature_inception, item_size=4)
418
+ composer.compose_numeric(self.key_tag, 2)
419
+ composer.compose_parsable(self.signers_name)
420
+ composer.compose_raw(self.signature)
421
+
422
+ return composer.composed_bytes
423
+
424
+
425
+ @attr.s
426
+ class DnsRecordMx(ParsableBase):
427
+ HEADER_SIZE = 2
428
+
429
+ priority = attr.ib(validator=attr.validators.instance_of(six.integer_types))
430
+ exchange = attr.ib(
431
+ converter=DnsNameUncompressed.convert,
432
+ validator=attr.validators.instance_of(DnsNameUncompressed)
433
+ )
434
+
435
+ @classmethod
436
+ def _parse(cls, parsable):
437
+ if len(parsable) < cls.HEADER_SIZE:
438
+ raise NotEnoughData(cls.HEADER_SIZE - len(parsable))
439
+
440
+ parser = ParserBinary(parsable)
441
+
442
+ parser.parse_numeric('priority', 2)
443
+ parser.parse_parsable('exchange', DnsNameUncompressed)
444
+
445
+ return cls(**parser), parser.parsed_length
446
+
447
+ def compose(self):
448
+ composer = ComposerBinary()
449
+
450
+ composer.compose_numeric(self.priority, 2)
451
+ composer.compose_parsable(self.exchange)
452
+
453
+ return composer.composed_bytes
454
+
455
+
456
+ @attr.s
457
+ class DnsRecordTxt(ParsableBase):
458
+ HEADER_SIZE = 1
459
+
460
+ value = attr.ib(validator=attr.validators.instance_of(six.string_types))
461
+
462
+ @classmethod
463
+ def _parse(cls, parsable):
464
+ if len(parsable) < cls.HEADER_SIZE:
465
+ raise NotEnoughData(cls.HEADER_SIZE - len(parsable))
466
+
467
+ parser = ParserBinary(parsable)
468
+
469
+ parser.parse_string('value', 1, encoding='ascii')
470
+
471
+ return cls(**parser), parser.parsed_length
472
+
473
+ def compose(self):
474
+ composer = ComposerBinary()
475
+
476
+ composer.compose_string(self.value, 'ascii', 1)
477
+
478
+ return composer.composed_bytes