cryptoparser 0.11.0__tar.gz → 0.11.2__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.
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CHANGELOG.rst +41 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2/CryptoParser.egg-info}/PKG-INFO +2 -2
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/requires.txt +1 -1
- {CryptoParser-0.11.0/CryptoParser.egg-info → CryptoParser-0.11.2}/PKG-INFO +2 -2
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/__setup__.py +1 -1
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/base.py +15 -2
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/field.py +213 -25
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/parse.py +41 -8
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/x509.py +22 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/header.py +481 -3
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/parse.py +18 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/key.py +172 -11
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/requirements.txt +1 -1
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CONTRIBUTING.rst +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/SOURCES.txt +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/dependency_links.txt +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/top_level.txt +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/LICENSE.txt +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/MANIFEST.in +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/README.rst +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/classes.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/exception.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/utils.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/record.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/txt.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/version.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/record.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/subprotocol.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/version.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/__init__.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/algorithm.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/ciphersuite.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/extension.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/grease.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/ldap.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/mysql.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/openvpn.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/postgresql.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/rdp.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/record.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/subprotocol.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/version.py +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/setup.cfg +0 -0
- {CryptoParser-0.11.0 → CryptoParser-0.11.2}/setup.py +0 -0
|
@@ -2,6 +2,47 @@
|
|
|
2
2
|
Changelog
|
|
3
3
|
=========
|
|
4
4
|
|
|
5
|
+
-------------------
|
|
6
|
+
0.11.2 - 2023-11-13
|
|
7
|
+
-------------------
|
|
8
|
+
|
|
9
|
+
Features
|
|
10
|
+
========
|
|
11
|
+
|
|
12
|
+
- HTTP(S) (``http``)
|
|
13
|
+
|
|
14
|
+
- Headers (``headers``)
|
|
15
|
+
|
|
16
|
+
- add parsers for generic headers
|
|
17
|
+
(`NEL <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/NEL>`__ (Network Error Logging),
|
|
18
|
+
`Set-Cookie <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie>`__)
|
|
19
|
+
- add parsers for security related headers
|
|
20
|
+
(`HTTP Public Key Pinning <https://en.wikipedia.org/wiki/HTTP_Public_Key_Pinning>`__ (HPKP),
|
|
21
|
+
`X-XSS-Protection <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection>`__)
|
|
22
|
+
|
|
23
|
+
Improvements
|
|
24
|
+
============
|
|
25
|
+
|
|
26
|
+
- HTTP(S) (``http``)
|
|
27
|
+
|
|
28
|
+
- Headers (``headers``)
|
|
29
|
+
|
|
30
|
+
- implement detailed parsing of
|
|
31
|
+
`Content-Type <https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Type>`__ header
|
|
32
|
+
|
|
33
|
+
-------------------
|
|
34
|
+
0.11.1 - 2023-11-06
|
|
35
|
+
-------------------
|
|
36
|
+
|
|
37
|
+
Features
|
|
38
|
+
========
|
|
39
|
+
|
|
40
|
+
- SSH (``ssh``)
|
|
41
|
+
|
|
42
|
+
- Public Keys (``pubkeys``)
|
|
43
|
+
|
|
44
|
+
- add X.509 certificate and certificate chain related classes (#63)
|
|
45
|
+
|
|
5
46
|
-------------------
|
|
6
47
|
0.11.0 - 2023-10-28
|
|
7
48
|
-------------------
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CryptoParser
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.2
|
|
4
4
|
Summary: Fast and flexible security protocol parser and generator
|
|
5
5
|
Author: Szilárd Pfeiffer
|
|
6
6
|
Author-email: coroner@pfeifferszilard.hu
|
|
@@ -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
|
|
51
|
+
Requires-Dist: cryptodatahub==0.11.2
|
|
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"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: CryptoParser
|
|
3
|
-
Version: 0.11.
|
|
3
|
+
Version: 0.11.2
|
|
4
4
|
Summary: Fast and flexible security protocol parser and generator
|
|
5
5
|
Author: Szilárd Pfeiffer
|
|
6
6
|
Author-email: coroner@pfeifferszilard.hu
|
|
@@ -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
|
|
51
|
+
Requires-Dist: cryptodatahub==0.11.2
|
|
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.11.
|
|
5
|
+
__version__ = '0.11.2'
|
|
6
6
|
__description__ = 'Fast and flexible security protocol parser and generator'
|
|
7
7
|
__author__ = 'Szilárd Pfeiffer'
|
|
8
8
|
__author_email__ = 'coroner@pfeifferszilard.hu'
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
# pylint: disable=too-many-lines
|
|
3
3
|
|
|
4
4
|
import abc
|
|
5
|
+
import datetime
|
|
5
6
|
import enum
|
|
6
7
|
import json
|
|
7
8
|
import math
|
|
@@ -18,6 +19,7 @@ from collections import OrderedDict
|
|
|
18
19
|
|
|
19
20
|
import attr
|
|
20
21
|
import six
|
|
22
|
+
import urllib3
|
|
21
23
|
|
|
22
24
|
from cryptodatahub.common.exception import InvalidValue
|
|
23
25
|
from cryptodatahub.common.grade import Gradeable
|
|
@@ -58,6 +60,12 @@ class SerializableTextEncoder(object):
|
|
|
58
60
|
|
|
59
61
|
|
|
60
62
|
class Serializable(object): # pylint: disable=too-few-public-methods
|
|
63
|
+
_MARKDOWN_RESULT_STRING_CLASSES = (
|
|
64
|
+
CryptoDataParamsBase,
|
|
65
|
+
ipaddress.IPv4Network,
|
|
66
|
+
ipaddress.IPv6Network,
|
|
67
|
+
urllib3.util.url.Url,
|
|
68
|
+
)
|
|
61
69
|
post_text_encoder = SerializableTextEncoder()
|
|
62
70
|
|
|
63
71
|
@staticmethod
|
|
@@ -172,6 +180,9 @@ class Serializable(object): # pylint: disable=too-few-public-methods
|
|
|
172
180
|
|
|
173
181
|
if hasattr(obj, '_asdict'):
|
|
174
182
|
dict_value = obj._asdict()
|
|
183
|
+
if not isinstance(dict_value, dict):
|
|
184
|
+
return False, dict_value
|
|
185
|
+
|
|
175
186
|
dict_value = Serializable._filter_out_non_human_friendly(obj, dict_value, human_friendly_only=True)
|
|
176
187
|
else:
|
|
177
188
|
dict_value = Serializable._get_ordered_dict(obj, human_friendly_only=True)
|
|
@@ -234,10 +245,12 @@ class Serializable(object): # pylint: disable=too-few-public-methods
|
|
|
234
245
|
return cls.post_text_encoder(obj.value, level)
|
|
235
246
|
|
|
236
247
|
return cls.post_text_encoder(obj.name, level)
|
|
237
|
-
elif isinstance(obj, (ipaddress.IPv4Network, ipaddress.IPv6Network)):
|
|
238
|
-
return False, str(obj)
|
|
239
248
|
elif attr.has(type(obj)):
|
|
240
249
|
result = cls._markdown_result_complex(obj, level)
|
|
250
|
+
elif isinstance(obj, cls._MARKDOWN_RESULT_STRING_CLASSES):
|
|
251
|
+
return False, str(obj)
|
|
252
|
+
elif isinstance(obj, datetime.timedelta):
|
|
253
|
+
return False, str(obj.seconds)
|
|
241
254
|
elif hasattr(obj, '_asdict'):
|
|
242
255
|
result = cls._markdown_result(obj._asdict(), level)
|
|
243
256
|
elif hasattr(obj, '__dict__') or isinstance(obj, dict):
|
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
import abc
|
|
4
4
|
import collections
|
|
5
5
|
import datetime
|
|
6
|
+
import json
|
|
6
7
|
|
|
7
8
|
import attr
|
|
8
9
|
import six
|
|
9
10
|
import urllib3
|
|
10
11
|
|
|
11
12
|
from cryptodatahub.common.exception import InvalidValue
|
|
12
|
-
from cryptodatahub.common.types import convert_url
|
|
13
|
+
from cryptodatahub.common.types import Base64Data, convert_base64_data, convert_value_to_object, convert_url
|
|
13
14
|
|
|
14
15
|
from cryptoparser.common.base import Serializable
|
|
15
16
|
from cryptoparser.common.exception import InvalidType, NotEnoughData
|
|
@@ -201,9 +202,11 @@ class FieldValueComponentBase(ParsableBase, Serializable):
|
|
|
201
202
|
raise NotImplementedError()
|
|
202
203
|
|
|
203
204
|
@classmethod
|
|
205
|
+
@abc.abstractmethod
|
|
204
206
|
def _parse(cls, parsable):
|
|
205
207
|
raise NotImplementedError()
|
|
206
208
|
|
|
209
|
+
@abc.abstractmethod
|
|
207
210
|
def compose(self):
|
|
208
211
|
raise NotImplementedError()
|
|
209
212
|
|
|
@@ -278,12 +281,15 @@ class FieldValueComponentKeyValueBase(FieldValueComponentBase):
|
|
|
278
281
|
def _parse_value(cls, parser):
|
|
279
282
|
raise NotImplementedError()
|
|
280
283
|
|
|
281
|
-
def
|
|
284
|
+
def _get_value_as_simple_type(self):
|
|
282
285
|
# neccessary only because PY2 handles multiple inheritance differently than PY3
|
|
283
286
|
if isinstance(self.value, ParsableBaseNoABC):
|
|
284
287
|
return self.value.compose().decode('ascii')
|
|
285
288
|
|
|
286
|
-
return
|
|
289
|
+
return self.value
|
|
290
|
+
|
|
291
|
+
def _get_value_as_str(self):
|
|
292
|
+
return str(self._get_value_as_simple_type())
|
|
287
293
|
|
|
288
294
|
@classmethod
|
|
289
295
|
def _parse(cls, parsable):
|
|
@@ -297,15 +303,22 @@ class FieldValueComponentKeyValueBase(FieldValueComponentBase):
|
|
|
297
303
|
|
|
298
304
|
cls._check_name(parser['name'])
|
|
299
305
|
|
|
300
|
-
|
|
306
|
+
if cls.get_canonical_name():
|
|
307
|
+
parser.parse_separator('=')
|
|
301
308
|
cls._parse_value(parser)
|
|
309
|
+
parsed_value = parser['value']
|
|
310
|
+
if cls.get_canonical_name():
|
|
311
|
+
parsed_value = cls(parsed_value)
|
|
302
312
|
|
|
303
|
-
return
|
|
313
|
+
return parsed_value, parser.parsed_length
|
|
304
314
|
|
|
305
315
|
def compose(self):
|
|
306
316
|
composer = ComposerText()
|
|
307
317
|
|
|
308
|
-
|
|
318
|
+
if self.get_canonical_name():
|
|
319
|
+
composer.compose_string_array([self.get_canonical_name(), self._get_value_as_str()], '=')
|
|
320
|
+
else:
|
|
321
|
+
composer.compose_string(self._get_value_as_str())
|
|
309
322
|
|
|
310
323
|
return composer.composed
|
|
311
324
|
|
|
@@ -377,6 +390,9 @@ class FieldValueComponentQuotedString(FieldValueComponentKeyValueBase):
|
|
|
377
390
|
def _get_value_as_str(self):
|
|
378
391
|
return '"{}"'.format(self.value)
|
|
379
392
|
|
|
393
|
+
def _get_value_as_simple_type(self):
|
|
394
|
+
return self.value
|
|
395
|
+
|
|
380
396
|
@classmethod
|
|
381
397
|
def _parse_value(cls, parser):
|
|
382
398
|
|
|
@@ -385,26 +401,96 @@ class FieldValueComponentQuotedString(FieldValueComponentKeyValueBase):
|
|
|
385
401
|
parser.parse_separator('"', 0, None)
|
|
386
402
|
|
|
387
403
|
|
|
404
|
+
@attr.s
|
|
405
|
+
class FieldValueComponentDateTime(FieldValueComponentKeyValueBase):
|
|
406
|
+
value = attr.ib(validator=attr.validators.instance_of(datetime.datetime))
|
|
407
|
+
|
|
408
|
+
@classmethod
|
|
409
|
+
@abc.abstractmethod
|
|
410
|
+
def get_canonical_name(cls):
|
|
411
|
+
raise NotImplementedError()
|
|
412
|
+
|
|
413
|
+
@classmethod
|
|
414
|
+
def _parse_value(cls, parser):
|
|
415
|
+
parser.parse_date_time('value')
|
|
416
|
+
|
|
417
|
+
def _get_value_as_simple_type(self):
|
|
418
|
+
return self.value.strftime('%a, %d %b %Y %H:%M:%S GMT')
|
|
419
|
+
|
|
420
|
+
|
|
388
421
|
@attr.s
|
|
389
422
|
class FieldValueComponentTimeDelta(FieldValueComponentKeyValueBase):
|
|
390
|
-
value = attr.ib(
|
|
423
|
+
value = attr.ib(
|
|
424
|
+
converter=convert_value_to_object(datetime.timedelta),
|
|
425
|
+
validator=attr.validators.instance_of(datetime.timedelta)
|
|
426
|
+
)
|
|
391
427
|
|
|
392
428
|
@classmethod
|
|
393
429
|
@abc.abstractmethod
|
|
394
430
|
def get_canonical_name(cls):
|
|
395
431
|
raise NotImplementedError()
|
|
396
432
|
|
|
433
|
+
@classmethod
|
|
434
|
+
def convert(cls, value):
|
|
435
|
+
if isinstance(value, cls):
|
|
436
|
+
return value
|
|
437
|
+
if isinstance(value, datetime.timedelta):
|
|
438
|
+
return cls(value)
|
|
439
|
+
|
|
440
|
+
return cls(datetime.timedelta(seconds=value))
|
|
441
|
+
|
|
397
442
|
@classmethod
|
|
398
443
|
def _parse_value(cls, parser):
|
|
399
444
|
parser.parse_time_delta('value')
|
|
400
445
|
|
|
401
|
-
def
|
|
402
|
-
return
|
|
446
|
+
def _get_value_as_simple_type(self):
|
|
447
|
+
return int(self.value.total_seconds())
|
|
403
448
|
|
|
404
449
|
def _as_markdown(self, level):
|
|
405
450
|
return self._markdown_result(str(self.value), level)
|
|
406
451
|
|
|
407
452
|
|
|
453
|
+
@attr.s
|
|
454
|
+
class FieldValueComponentStringBase64(FieldValueComponentQuotedString):
|
|
455
|
+
value = attr.ib(
|
|
456
|
+
converter=convert_base64_data(),
|
|
457
|
+
validator=attr.validators.instance_of(Base64Data)
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
@classmethod
|
|
461
|
+
@abc.abstractmethod
|
|
462
|
+
def get_canonical_name(cls):
|
|
463
|
+
raise NotImplementedError()
|
|
464
|
+
|
|
465
|
+
@classmethod
|
|
466
|
+
def _check_name(cls, name):
|
|
467
|
+
cls._check_name_insensitive(name)
|
|
468
|
+
|
|
469
|
+
@classmethod
|
|
470
|
+
def _parse_value(cls, parser):
|
|
471
|
+
parser.parse_string_by_length('value')
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
@attr.s
|
|
475
|
+
class FieldValueComponentBool(FieldValueComponentKeyValueBase):
|
|
476
|
+
value = attr.ib(validator=attr.validators.instance_of(bool))
|
|
477
|
+
|
|
478
|
+
@classmethod
|
|
479
|
+
@abc.abstractmethod
|
|
480
|
+
def get_canonical_name(cls):
|
|
481
|
+
raise NotImplementedError()
|
|
482
|
+
|
|
483
|
+
def _get_value_as_str(self):
|
|
484
|
+
return 'yes' if self.value else 'no'
|
|
485
|
+
|
|
486
|
+
def _get_value_as_simple_type(self):
|
|
487
|
+
return self.value
|
|
488
|
+
|
|
489
|
+
@classmethod
|
|
490
|
+
def _parse_value(cls, parser):
|
|
491
|
+
parser.parse_bool('value')
|
|
492
|
+
|
|
493
|
+
|
|
408
494
|
@attr.s
|
|
409
495
|
class FieldValueComponentNumber(FieldValueComponentKeyValueBase):
|
|
410
496
|
value = attr.ib(validator=attr.validators.instance_of(int))
|
|
@@ -419,6 +505,26 @@ class FieldValueComponentNumber(FieldValueComponentKeyValueBase):
|
|
|
419
505
|
parser.parse_numeric('value')
|
|
420
506
|
|
|
421
507
|
|
|
508
|
+
@attr.s
|
|
509
|
+
class FieldValueComponentFloat(FieldValueComponentNumber):
|
|
510
|
+
value = attr.ib(
|
|
511
|
+
converter=float,
|
|
512
|
+
validator=attr.validators.instance_of(float)
|
|
513
|
+
)
|
|
514
|
+
|
|
515
|
+
@classmethod
|
|
516
|
+
@abc.abstractmethod
|
|
517
|
+
def get_canonical_name(cls):
|
|
518
|
+
raise NotImplementedError()
|
|
519
|
+
|
|
520
|
+
def _get_value_as_simple_type(self):
|
|
521
|
+
return self.value
|
|
522
|
+
|
|
523
|
+
@classmethod
|
|
524
|
+
def _parse_value(cls, parser):
|
|
525
|
+
parser.parse_float('value')
|
|
526
|
+
|
|
527
|
+
|
|
422
528
|
@attr.s
|
|
423
529
|
class FieldValueComponentPercent(FieldValueComponentNumber):
|
|
424
530
|
def __attrs_post_init__(self):
|
|
@@ -431,6 +537,57 @@ class FieldValueComponentPercent(FieldValueComponentNumber):
|
|
|
431
537
|
raise NotImplementedError()
|
|
432
538
|
|
|
433
539
|
|
|
540
|
+
@attr.s
|
|
541
|
+
class FieldValueComponentStringEnumParams(object):
|
|
542
|
+
code = attr.ib(validator=attr.validators.instance_of(six.string_types))
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
@attr.s
|
|
546
|
+
class FieldValueComponentStringEnum(FieldValueComponentKeyValueBase):
|
|
547
|
+
value = attr.ib()
|
|
548
|
+
|
|
549
|
+
@classmethod
|
|
550
|
+
@abc.abstractmethod
|
|
551
|
+
def get_canonical_name(cls):
|
|
552
|
+
raise NotImplementedError()
|
|
553
|
+
|
|
554
|
+
@classmethod
|
|
555
|
+
@abc.abstractmethod
|
|
556
|
+
def _get_value_type(cls):
|
|
557
|
+
raise NotImplementedError()
|
|
558
|
+
|
|
559
|
+
@value.validator
|
|
560
|
+
def _validator_value(self, _, value):
|
|
561
|
+
if not isinstance(value, self._get_value_type()):
|
|
562
|
+
raise InvalidValue(value, type(self), 'value')
|
|
563
|
+
|
|
564
|
+
@classmethod
|
|
565
|
+
def _parse_value(cls, parser):
|
|
566
|
+
try:
|
|
567
|
+
parser.parse_parsable('value', cls._get_value_type())
|
|
568
|
+
except InvalidValue as e:
|
|
569
|
+
six.raise_from(InvalidValue(e.value.decode('ascii'), cls, 'value'), e)
|
|
570
|
+
|
|
571
|
+
def _get_value_as_simple_type(self):
|
|
572
|
+
return self.value.value.code
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
@attr.s
|
|
576
|
+
class FieldValueComponentStringEnumOption(FieldValueComponentStringEnum):
|
|
577
|
+
@classmethod
|
|
578
|
+
@abc.abstractmethod
|
|
579
|
+
def _get_value_type(cls):
|
|
580
|
+
raise NotImplementedError()
|
|
581
|
+
|
|
582
|
+
@classmethod
|
|
583
|
+
def get_canonical_name(cls):
|
|
584
|
+
return ''
|
|
585
|
+
|
|
586
|
+
@classmethod
|
|
587
|
+
def _check_name(cls, name):
|
|
588
|
+
pass
|
|
589
|
+
|
|
590
|
+
|
|
434
591
|
@attr.s
|
|
435
592
|
class FieldValueComponentString(FieldValueComponentKeyValueBase):
|
|
436
593
|
value = attr.ib(validator=attr.validators.optional(attr.validators.instance_of(six.string_types)))
|
|
@@ -467,7 +624,7 @@ class FieldValueComponentUrl(FieldValueComponentKeyValueBase):
|
|
|
467
624
|
def _parse_value(cls, parser):
|
|
468
625
|
parser.parse_string_by_length('value', item_class=convert_url())
|
|
469
626
|
|
|
470
|
-
def
|
|
627
|
+
def _get_value_as_simple_type(self):
|
|
471
628
|
if self.value.scheme == 'mailto':
|
|
472
629
|
value = 'mailto:' + self.value.path[1:]
|
|
473
630
|
else:
|
|
@@ -476,7 +633,7 @@ class FieldValueComponentUrl(FieldValueComponentKeyValueBase):
|
|
|
476
633
|
return value
|
|
477
634
|
|
|
478
635
|
def _as_markdown(self, level):
|
|
479
|
-
return self._markdown_result(self.
|
|
636
|
+
return self._markdown_result(self._get_value_as_simple_type(), level)
|
|
480
637
|
|
|
481
638
|
|
|
482
639
|
class FieldValueBase(ParsableBase, Serializable):
|
|
@@ -489,25 +646,54 @@ class FieldValueBase(ParsableBase, Serializable):
|
|
|
489
646
|
def compose(self):
|
|
490
647
|
raise NotImplementedError()
|
|
491
648
|
|
|
492
|
-
|
|
493
|
-
class FieldValueMultiple(FieldValueBase):
|
|
494
|
-
@classmethod
|
|
495
|
-
@abc.abstractmethod
|
|
496
|
-
def _get_header_value_list_class(cls):
|
|
497
|
-
raise NotImplementedError()
|
|
498
|
-
|
|
499
649
|
@classmethod
|
|
500
|
-
def
|
|
501
|
-
attr_to_component_name_dict =
|
|
650
|
+
def _get_attr_to_validator_type_dict(cls, attr_fields_dict):
|
|
651
|
+
attr_to_component_name_dict = []
|
|
502
652
|
|
|
503
653
|
for attribute in attr_fields_dict.values():
|
|
504
654
|
validator = attribute.validator
|
|
505
655
|
if is_validator_optional(validator):
|
|
506
656
|
validator = validator.validator
|
|
507
657
|
|
|
508
|
-
attr_to_component_name_dict
|
|
658
|
+
attr_to_component_name_dict.append((attribute.name, validator.type))
|
|
659
|
+
|
|
660
|
+
return collections.OrderedDict(attr_to_component_name_dict)
|
|
509
661
|
|
|
510
|
-
|
|
662
|
+
|
|
663
|
+
class FieldsJson(FieldValueBase):
|
|
664
|
+
@classmethod
|
|
665
|
+
def _parse(cls, parsable):
|
|
666
|
+
try:
|
|
667
|
+
raw_values = json.loads(parsable.decode('ascii'), object_pairs_hook=collections.OrderedDict)
|
|
668
|
+
except ValueError as e: # json.decoder.JSONDecodeError is derived from ValueError
|
|
669
|
+
six.raise_from(InvalidValue(six.ensure_text(parsable, 'ascii'), cls, 'value'), e)
|
|
670
|
+
|
|
671
|
+
attr_fields_dict = attr.fields_dict(cls)
|
|
672
|
+
|
|
673
|
+
return cls(**{
|
|
674
|
+
attribute_name: raw_values[validator_class.get_canonical_name()]
|
|
675
|
+
for attribute_name, validator_class in cls._get_attr_to_validator_type_dict(attr_fields_dict).items()
|
|
676
|
+
if validator_class.get_canonical_name() in raw_values
|
|
677
|
+
}), len(parsable)
|
|
678
|
+
|
|
679
|
+
def compose(self):
|
|
680
|
+
attr_fields_dict = attr.fields_dict(type(self))
|
|
681
|
+
|
|
682
|
+
return json.dumps(collections.OrderedDict([
|
|
683
|
+
(
|
|
684
|
+
validator_class.get_canonical_name(),
|
|
685
|
+
getattr(self, attribute_name)._get_value_as_simple_type() # pylint: disable=protected-access
|
|
686
|
+
)
|
|
687
|
+
for attribute_name, validator_class in self._get_attr_to_validator_type_dict(attr_fields_dict).items()
|
|
688
|
+
if getattr(self, attribute_name) is not None
|
|
689
|
+
])).encode('ascii')
|
|
690
|
+
|
|
691
|
+
|
|
692
|
+
class FieldValueMultiple(FieldValueBase):
|
|
693
|
+
@classmethod
|
|
694
|
+
@abc.abstractmethod
|
|
695
|
+
def _get_header_value_list_class(cls):
|
|
696
|
+
raise NotImplementedError()
|
|
511
697
|
|
|
512
698
|
@classmethod
|
|
513
699
|
def _parse_basic_params(cls, attr_to_component_name_dict, attr_fields_dict, components, params):
|
|
@@ -524,10 +710,12 @@ class FieldValueMultiple(FieldValueBase):
|
|
|
524
710
|
if attribute.default == attr.NOTHING:
|
|
525
711
|
raise InvalidValue(None, cls, name)
|
|
526
712
|
|
|
713
|
+
component = attribute.default
|
|
714
|
+
|
|
527
715
|
if attr_to_component_name_dict[name].get_canonical_name() in components:
|
|
528
716
|
parsable = components.pop(attr_to_component_name_dict[name].get_canonical_name())
|
|
529
|
-
if parsable is None: # value is None in
|
|
530
|
-
parsable =
|
|
717
|
+
if parsable is None: # value is None in case of optional values
|
|
718
|
+
parsable = component
|
|
531
719
|
else:
|
|
532
720
|
parsable = '='.join([attr_to_component_name_dict[name].get_canonical_name(), parsable])
|
|
533
721
|
params[name] = attr_to_component_name_dict[name].parse_exact_size(six.ensure_binary(parsable, 'ascii'))
|
|
@@ -553,7 +741,7 @@ class FieldValueMultiple(FieldValueBase):
|
|
|
553
741
|
extension = (name, attribute)
|
|
554
742
|
else:
|
|
555
743
|
raise NotImplementedError()
|
|
556
|
-
attr_to_component_name_dict = cls.
|
|
744
|
+
attr_to_component_name_dict = cls._get_attr_to_validator_type_dict(attr_fields_dict)
|
|
557
745
|
|
|
558
746
|
components = cls._get_header_value_list_class().parse_exact_size(parsable).value
|
|
559
747
|
|
|
@@ -206,8 +206,10 @@ class ParserText(ParserBase):
|
|
|
206
206
|
'separator', self._parsed_length, separator, min_length, max_length
|
|
207
207
|
)
|
|
208
208
|
|
|
209
|
-
def _parse_numeric_array(
|
|
209
|
+
def _parse_numeric_array( # pylint: disable=too-many-arguments
|
|
210
|
+
self, name, item_num, separator, converter, is_floating):
|
|
210
211
|
value = []
|
|
212
|
+
floating_point_found = False
|
|
211
213
|
last_item_offset = self._parsed_length
|
|
212
214
|
item_offset = self._parsed_length
|
|
213
215
|
while True:
|
|
@@ -217,8 +219,14 @@ class ParserText(ParserBase):
|
|
|
217
219
|
if item_offset == last_item_offset:
|
|
218
220
|
raise InvalidValue(self._parsable[self._parsed_length:], type(self), name)
|
|
219
221
|
|
|
220
|
-
if
|
|
221
|
-
|
|
222
|
+
if (is_floating and
|
|
223
|
+
not floating_point_found and item_offset < len(self._parsable) and
|
|
224
|
+
six.indexbytes(self._parsable, item_offset) == ord('.')):
|
|
225
|
+
item_offset += 1
|
|
226
|
+
floating_point_found = True
|
|
227
|
+
continue
|
|
228
|
+
|
|
229
|
+
value.append(converter(self._parsable[last_item_offset:item_offset]))
|
|
222
230
|
|
|
223
231
|
if item_offset == len(self._parsable) or (item_num is not None and len(value) == item_num):
|
|
224
232
|
break
|
|
@@ -230,20 +238,38 @@ class ParserText(ParserBase):
|
|
|
230
238
|
six.raise_from(InvalidValue(self._parsable[self._parsed_length:item_offset], type(self), name), e)
|
|
231
239
|
|
|
232
240
|
last_item_offset = item_offset
|
|
241
|
+
floating_point_found = False
|
|
233
242
|
|
|
234
243
|
return value, item_offset - self._parsed_length
|
|
235
244
|
|
|
236
245
|
def parse_numeric(self, name, converter=int):
|
|
237
|
-
value, parsed_length = self._parse_numeric_array(name, 1, None, converter)
|
|
246
|
+
value, parsed_length = self._parse_numeric_array(name, 1, None, converter, False)
|
|
247
|
+
self._parsed_values[name] = value[0]
|
|
248
|
+
self._parsed_length += parsed_length
|
|
249
|
+
|
|
250
|
+
def parse_float(self, name, converter=float):
|
|
251
|
+
value, parsed_length = self._parse_numeric_array(name, 1, None, converter, True)
|
|
238
252
|
self._parsed_values[name] = value[0]
|
|
239
253
|
self._parsed_length += parsed_length
|
|
240
254
|
|
|
241
255
|
def parse_numeric_array(self, name, item_num, separator, converter=int):
|
|
242
|
-
value, parsed_length = self._parse_numeric_array(name, item_num, separator, converter)
|
|
256
|
+
value, parsed_length = self._parse_numeric_array(name, item_num, separator, converter, False)
|
|
243
257
|
|
|
244
258
|
self._parsed_values[name] = value
|
|
245
259
|
self._parsed_length += parsed_length
|
|
246
260
|
|
|
261
|
+
def parse_bool(self, name):
|
|
262
|
+
for string_value, bool_value in (('yes', True), ('no', False)):
|
|
263
|
+
try:
|
|
264
|
+
self.parse_string(name, string_value)
|
|
265
|
+
except InvalidValue:
|
|
266
|
+
pass
|
|
267
|
+
else:
|
|
268
|
+
self._parsed_values[name] = bool_value
|
|
269
|
+
break
|
|
270
|
+
else:
|
|
271
|
+
raise InvalidValue(self._parsable[self._parsed_length:], type(self), name)
|
|
272
|
+
|
|
247
273
|
def parse_string(self, name, value):
|
|
248
274
|
min_length = len(value)
|
|
249
275
|
max_length = min_length
|
|
@@ -442,7 +468,7 @@ class ParserText(ParserBase):
|
|
|
442
468
|
self._parsed_length = len(self._parsable)
|
|
443
469
|
|
|
444
470
|
def parse_time_delta(self, name):
|
|
445
|
-
value, parsed_length = self._parse_numeric_array(name, 1, None, int)
|
|
471
|
+
value, parsed_length = self._parse_numeric_array(name, 1, None, int, False)
|
|
446
472
|
|
|
447
473
|
try:
|
|
448
474
|
time_delta = datetime.timedelta(seconds=value[0])
|
|
@@ -586,10 +612,14 @@ class ParserBinary(ParserBase):
|
|
|
586
612
|
six.raise_from(InvalidValue(value, converter, name), e)
|
|
587
613
|
self._parsed_length += len(parsed_bytes)
|
|
588
614
|
|
|
589
|
-
def parse_raw(self, name, size):
|
|
615
|
+
def parse_raw(self, name, size, converter=bytearray):
|
|
590
616
|
parsed_bytes = self._parse_bytes(size)
|
|
591
617
|
|
|
592
|
-
|
|
618
|
+
try:
|
|
619
|
+
self._parsed_values[name] = converter(parsed_bytes)
|
|
620
|
+
except ValueError as e:
|
|
621
|
+
six.raise_from(InvalidValue(parsed_bytes, converter, name), e)
|
|
622
|
+
|
|
593
623
|
self._parsed_length += size
|
|
594
624
|
|
|
595
625
|
def parse_string(self, name, item_size, encoding, converter=str):
|
|
@@ -753,6 +783,9 @@ class ComposerText(ComposerBase):
|
|
|
753
783
|
def compose_numeric_array(self, values, separator):
|
|
754
784
|
self._compose_numeric_array(values, separator)
|
|
755
785
|
|
|
786
|
+
def compose_bool(self, value):
|
|
787
|
+
self.compose_string('yes' if value else 'no')
|
|
788
|
+
|
|
756
789
|
def compose_string(self, value):
|
|
757
790
|
self._compose_string_array([value, ], encoding=self._encoding, separator='')
|
|
758
791
|
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
# -*- coding: utf-8 -*-
|
|
2
2
|
|
|
3
|
+
import collections
|
|
3
4
|
import datetime
|
|
4
5
|
import enum
|
|
5
6
|
|
|
6
7
|
import attr
|
|
7
8
|
|
|
9
|
+
import asn1crypto
|
|
10
|
+
|
|
11
|
+
from cryptodatahub.common.key import PublicKeyX509Base
|
|
8
12
|
from cryptodatahub.common.stores import CertificateTransparencyLog, CertificateTransparencyLogParamsBase
|
|
9
13
|
|
|
10
14
|
from cryptoparser.common.base import (
|
|
@@ -99,3 +103,21 @@ class SignedCertificateTimestampList(VectorParsable):
|
|
|
99
103
|
fallback_class=None,
|
|
100
104
|
min_byte_num=0, max_byte_num=2 ** 16 - 1
|
|
101
105
|
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class PublicKeyX509(PublicKeyX509Base):
|
|
109
|
+
@property
|
|
110
|
+
def signed_certificate_timestamps(self):
|
|
111
|
+
for extension in self._certificate['tbs_certificate']['extensions']:
|
|
112
|
+
if extension['extn_id'].dotted == '1.3.6.1.4.1.11129.2.4.2':
|
|
113
|
+
asn1_value = asn1crypto.core.load(bytes(extension['extn_value']))
|
|
114
|
+
return SignedCertificateTimestampList.parse_exact_size(bytes(asn1_value))
|
|
115
|
+
|
|
116
|
+
return SignedCertificateTimestampList([])
|
|
117
|
+
|
|
118
|
+
def _asdict(self):
|
|
119
|
+
dict_value = super(PublicKeyX509, self)._asdict()
|
|
120
|
+
|
|
121
|
+
return collections.OrderedDict(list(dict_value.items()) + [
|
|
122
|
+
('signed_certificate_timestamps', self.signed_certificate_timestamps),
|
|
123
|
+
])
|