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.
Files changed (49) hide show
  1. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CHANGELOG.rst +41 -0
  2. {CryptoParser-0.11.0 → CryptoParser-0.11.2/CryptoParser.egg-info}/PKG-INFO +2 -2
  3. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/requires.txt +1 -1
  4. {CryptoParser-0.11.0/CryptoParser.egg-info → CryptoParser-0.11.2}/PKG-INFO +2 -2
  5. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/__setup__.py +1 -1
  6. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/base.py +15 -2
  7. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/field.py +213 -25
  8. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/parse.py +41 -8
  9. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/x509.py +22 -0
  10. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/header.py +481 -3
  11. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/parse.py +18 -0
  12. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/key.py +172 -11
  13. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/requirements.txt +1 -1
  14. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CONTRIBUTING.rst +0 -0
  15. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/SOURCES.txt +0 -0
  16. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/dependency_links.txt +0 -0
  17. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/CryptoParser.egg-info/top_level.txt +0 -0
  18. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/LICENSE.txt +0 -0
  19. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/MANIFEST.in +0 -0
  20. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/README.rst +0 -0
  21. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/__init__.py +0 -0
  22. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/__init__.py +0 -0
  23. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/classes.py +0 -0
  24. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/exception.py +0 -0
  25. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/common/utils.py +0 -0
  26. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/__init__.py +0 -0
  27. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/record.py +0 -0
  28. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/dnsrec/txt.py +0 -0
  29. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/__init__.py +0 -0
  30. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/httpx/version.py +0 -0
  31. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/__init__.py +0 -0
  32. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/record.py +0 -0
  33. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/subprotocol.py +0 -0
  34. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/ssh/version.py +0 -0
  35. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/__init__.py +0 -0
  36. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/algorithm.py +0 -0
  37. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/ciphersuite.py +0 -0
  38. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/extension.py +0 -0
  39. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/grease.py +0 -0
  40. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/ldap.py +0 -0
  41. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/mysql.py +0 -0
  42. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/openvpn.py +0 -0
  43. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/postgresql.py +0 -0
  44. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/rdp.py +0 -0
  45. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/record.py +0 -0
  46. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/subprotocol.py +0 -0
  47. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/cryptoparser/tls/version.py +0 -0
  48. {CryptoParser-0.11.0 → CryptoParser-0.11.2}/setup.cfg +0 -0
  49. {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.0
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>=0.11.0
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
  asn1crypto
2
2
  attrs<22.0.1,>=20.3.0
3
- cryptodatahub>=0.11.0
3
+ cryptodatahub==0.11.2
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.11.0
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>=0.11.0
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.0'
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 _get_value_as_str(self):
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 str(self.value)
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
- parser.parse_separator('=')
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 cls(parser['value']), parser.parsed_length
313
+ return parsed_value, parser.parsed_length
304
314
 
305
315
  def compose(self):
306
316
  composer = ComposerText()
307
317
 
308
- composer.compose_string_array([self.get_canonical_name(), self._get_value_as_str()], '=')
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(validator=attr.validators.instance_of(datetime.timedelta))
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 _get_value_as_str(self):
402
- return str(int(self.value.total_seconds()))
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 _get_value_as_str(self):
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._get_value_as_str(), level)
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 _get_attr_to_component_name_dict(cls, attr_fields_dict):
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[attribute.name] = validator.type
658
+ attr_to_component_name_dict.append((attribute.name, validator.type))
659
+
660
+ return collections.OrderedDict(attr_to_component_name_dict)
509
661
 
510
- return attr_to_component_name_dict
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 case of optional values
530
- parsable = attr_to_component_name_dict[name].get_canonical_name()
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._get_attr_to_component_name_dict(attr_fields_dict)
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(self, name, item_num, separator, converter):
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 item_offset != last_item_offset:
221
- value.append(converter(self._parsable[last_item_offset:item_offset]))
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
- self._parsed_values[name] = bytearray(parsed_bytes)
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
+ ])