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

135 lines
4.1 KiB

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