Reference implementations of PQC
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

146 lines
4.7 KiB

  1. """
  2. Verify the metadata specified in the META.yml files.
  3. """
  4. import copy
  5. import itertools
  6. import pytest
  7. import helpers
  8. import pqclean
  9. @pytest.mark.parametrize(
  10. 'scheme',
  11. pqclean.Scheme.all_schemes(),
  12. ids=str,
  13. )
  14. @helpers.filtered_test
  15. def test_metadata(scheme):
  16. metadata = scheme.metadata()
  17. specification = EXPECTED_FIELDS.items()
  18. if scheme.type == 'kem':
  19. specification = itertools.chain(specification, KEM_FIELDS.items())
  20. elif scheme.type == 'sign':
  21. specification = itertools.chain(specification,
  22. SIGNATURE_FIELDS.items())
  23. else:
  24. assert False, "Wrong type of metadata"
  25. check_spec(copy.deepcopy(metadata), specification)
  26. implementation_names_in_yaml = set(
  27. i['name'] for i in metadata['implementations'])
  28. if len(implementation_names_in_yaml) != len(metadata['implementations']):
  29. raise AssertionError("Implementations in YAML file are not distinct")
  30. implementations_on_disk = set(i.name for i in scheme.implementations)
  31. if implementation_names_in_yaml != implementations_on_disk:
  32. raise AssertionError("Implementations in YAML file {} and "
  33. "implementations on disk {} do not match"
  34. .format(implementation_names_in_yaml,
  35. implementations_on_disk))
  36. EXPECTED_FIELDS = {
  37. 'name': {'type': str},
  38. 'type': {'type': str},
  39. 'claimed-nist-level': {'type': int, 'min': 1, 'max': 5},
  40. 'length-public-key': {'type': int, 'min': 1},
  41. 'length-secret-key': {'type': int, 'min': 1},
  42. 'nistkat-sha256': {'type': str, 'length': 64},
  43. 'principal-submitters': {'type': list, 'elements': {'type': str}},
  44. 'auxiliary-submitters': {
  45. 'type': list, 'elements': {'type': str}, 'optional': True},
  46. 'implementations': {
  47. 'type': list,
  48. 'elements': {
  49. 'type': dict,
  50. 'spec': {
  51. 'name': {'type': str},
  52. 'version': {'type': str},
  53. },
  54. },
  55. },
  56. }
  57. KEM_FIELDS = {
  58. 'claimed-security': {'type': str, 'values': ['IND-CPA', 'IND-CCA2']},
  59. 'length-ciphertext': {'type': int, 'min': 1},
  60. 'length-shared-secret': {'type': int, 'min': 1},
  61. }
  62. SIGNATURE_FIELDS = {
  63. 'length-signature': {'type': int, 'min': 1},
  64. 'testvectors-sha256': {'type': str, 'length': 64},
  65. }
  66. def check_spec(metadata, spec):
  67. for field, props in spec:
  68. if field not in metadata and 'optional' not in props:
  69. raise AssertionError("Field '{}' not present.".format(field))
  70. # validate element
  71. if field in metadata:
  72. check_element(field, metadata[field], props)
  73. # delete it to detect extras
  74. del metadata[field]
  75. # Done checking all specified fields, check if we have extras
  76. for field, value in metadata.items():
  77. raise AssertionError(
  78. "Unexpected item '{}' with value '{}'".format(field, value))
  79. def check_element(field, element, props):
  80. type_ = props['type']
  81. # Validate type of element
  82. type_(element)
  83. # Strs are valid lists otherwise
  84. if type_ == list and type(element) != list:
  85. raise ValueError("Field {} not a list".format(field))
  86. # lists are valid dicts otherwise
  87. if type_ == dict and type(element) != dict:
  88. raise ValueError("Field {} not a dict".format(field))
  89. if type_ == int:
  90. element = int(element)
  91. if 'min' in props:
  92. if element < props['min']:
  93. raise ValueError("Value of field '{}' is lower than minimum "
  94. "value {}".format(field, props['min']))
  95. if 'max' in props:
  96. if element > props['max']:
  97. raise ValueError("Value of field '{}' is larger than maximum"
  98. " value {}"
  99. .format(field, props['max']))
  100. if type_ == str:
  101. if 'length' in props:
  102. actual_len = len(element)
  103. if actual_len != props['length']:
  104. raise ValueError("Value of field '{}' should be length {}"
  105. " but was length {}"
  106. .format(field, props['length'], actual_len))
  107. if 'values' in props and element not in props['values']:
  108. raise ValueError("'{}' should be in {}"
  109. .format(element, props['values']))
  110. if type_ == list: # recursively check the elements
  111. for el in element:
  112. check_element('element of {}'.format(field), el, props['elements'])
  113. if type_ == dict:
  114. check_spec(element, props['spec'].items())
  115. if __name__ == '__main__':
  116. import sys
  117. pytest.main(sys.argv)