loader.py 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207
  1. # ===================================================================
  2. #
  3. # Copyright (c) 2016, Legrandin <helderijs@gmail.com>
  4. # All rights reserved.
  5. #
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions
  8. # are met:
  9. #
  10. # 1. Redistributions of source code must retain the above copyright
  11. # notice, this list of conditions and the following disclaimer.
  12. # 2. Redistributions in binary form must reproduce the above copyright
  13. # notice, this list of conditions and the following disclaimer in
  14. # the documentation and/or other materials provided with the
  15. # distribution.
  16. #
  17. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  18. # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  19. # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  20. # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  21. # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  22. # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  23. # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  25. # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  26. # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  27. # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  28. # POSSIBILITY OF SUCH DAMAGE.
  29. # ===================================================================
  30. import os
  31. import re
  32. import json
  33. import errno
  34. import binascii
  35. import warnings
  36. from binascii import unhexlify
  37. from Cryptodome.Util.py3compat import FileNotFoundError
  38. try:
  39. import pycryptodome_test_vectors # type: ignore
  40. test_vectors_available = True
  41. except ImportError:
  42. test_vectors_available = False
  43. def _load_tests(dir_comps, file_in, description, conversions):
  44. """Load and parse a test vector file
  45. Return a list of objects, one per group of adjacent
  46. KV lines or for a single line in the form "[.*]".
  47. For a group of lines, the object has one attribute per line.
  48. """
  49. line_number = 0
  50. results = []
  51. class TestVector(object):
  52. def __init__(self, description, count):
  53. self.desc = description
  54. self.count = count
  55. self.others = []
  56. test_vector = None
  57. count = 0
  58. new_group = True
  59. while True:
  60. line_number += 1
  61. line = file_in.readline()
  62. if not line:
  63. if test_vector is not None:
  64. results.append(test_vector)
  65. break
  66. line = line.strip()
  67. # Skip comments and empty lines
  68. if line.startswith('#') or not line:
  69. new_group = True
  70. continue
  71. if line.startswith("["):
  72. if test_vector is not None:
  73. results.append(test_vector)
  74. test_vector = None
  75. results.append(line)
  76. continue
  77. if new_group:
  78. count += 1
  79. new_group = False
  80. if test_vector is not None:
  81. results.append(test_vector)
  82. test_vector = TestVector("%s (#%d)" % (description, count), count)
  83. res = re.match("([A-Za-z0-9]+) = ?(.*)", line)
  84. if not res:
  85. test_vector.others += [line]
  86. else:
  87. token = res.group(1).lower()
  88. data = res.group(2).lower()
  89. conversion = conversions.get(token, None)
  90. if conversion is None:
  91. if len(data) % 2 != 0:
  92. data = "0" + data
  93. setattr(test_vector, token, binascii.unhexlify(data))
  94. else:
  95. setattr(test_vector, token, conversion(data))
  96. # This line is ignored
  97. return results
  98. def load_test_vectors(dir_comps, file_name, description, conversions):
  99. """Load and parse a test vector file
  100. This function returns a list of objects, one per group of adjacent
  101. KV lines or for a single line in the form "[.*]".
  102. For a group of lines, the object has one attribute per line.
  103. """
  104. results = None
  105. try:
  106. if not test_vectors_available:
  107. raise FileNotFoundError(errno.ENOENT,
  108. os.strerror(errno.ENOENT),
  109. file_name)
  110. description = "%s test (%s)" % (description, file_name)
  111. init_dir = os.path.dirname(pycryptodome_test_vectors.__file__)
  112. full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name)
  113. with open(full_file_name) as file_in:
  114. results = _load_tests(dir_comps, file_in, description, conversions)
  115. except FileNotFoundError:
  116. warnings.warn("Warning: skipping extended tests for " + description,
  117. UserWarning,
  118. stacklevel=2)
  119. return results
  120. def load_test_vectors_wycheproof(dir_comps, file_name, description,
  121. root_tag={}, group_tag={}, unit_tag={}):
  122. result = []
  123. try:
  124. if not test_vectors_available:
  125. raise FileNotFoundError(errno.ENOENT,
  126. os.strerror(errno.ENOENT),
  127. file_name)
  128. init_dir = os.path.dirname(pycryptodome_test_vectors.__file__)
  129. full_file_name = os.path.join(os.path.join(init_dir, *dir_comps), file_name)
  130. with open(full_file_name) as file_in:
  131. tv_tree = json.load(file_in)
  132. except FileNotFoundError:
  133. warnings.warn("Warning: skipping extended tests for " + description,
  134. UserWarning,
  135. stacklevel=2)
  136. return result
  137. class TestVector(object):
  138. pass
  139. common_root = {}
  140. for k, v in root_tag.items():
  141. common_root[k] = v(tv_tree)
  142. for group in tv_tree['testGroups']:
  143. common_group = {}
  144. for k, v in group_tag.items():
  145. common_group[k] = v(group)
  146. for test in group['tests']:
  147. tv = TestVector()
  148. for k, v in common_root.items():
  149. setattr(tv, k, v)
  150. for k, v in common_group.items():
  151. setattr(tv, k, v)
  152. tv.id = test['tcId']
  153. tv.comment = test['comment']
  154. for attr in 'key', 'iv', 'aad', 'msg', 'ct', 'tag', 'label', 'ikm', 'salt', 'info', 'okm', 'sig':
  155. if attr in test:
  156. setattr(tv, attr, unhexlify(test[attr]))
  157. tv.filename = file_name
  158. for k, v in unit_tag.items():
  159. setattr(tv, k, v(test))
  160. tv.valid = test['result'] != "invalid"
  161. tv.warning = test['result'] == "acceptable"
  162. result.append(tv)
  163. return result