選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

147 行
4.7 KiB

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