Reference implementations of PQC
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

209 lines
6.3 KiB

  1. import glob
  2. import os
  3. from typing import Optional
  4. from functools import lru_cache
  5. import yaml
  6. import platform
  7. import helpers
  8. class Scheme:
  9. def __init__(self):
  10. self.type = None
  11. self.name = None
  12. self.implementations = []
  13. def path(self, base='..'):
  14. return os.path.join(base, 'crypto_' + self.type, self.name)
  15. def namespace_prefix(self):
  16. return 'PQCLEAN_{}_'.format(self.name.upper()).replace('-', '')
  17. @staticmethod
  18. @lru_cache(maxsize=None)
  19. def by_name(scheme_name):
  20. for scheme in Scheme.all_schemes():
  21. if scheme.name == scheme_name:
  22. return scheme
  23. raise KeyError()
  24. @staticmethod
  25. @lru_cache(maxsize=1)
  26. def all_schemes():
  27. schemes = []
  28. schemes.extend(Scheme.all_schemes_of_type('kem'))
  29. schemes.extend(Scheme.all_schemes_of_type('sign'))
  30. return schemes
  31. @staticmethod
  32. @lru_cache(maxsize=1)
  33. def all_implementations():
  34. implementations = []
  35. for scheme in Scheme.all_schemes():
  36. implementations.extend(scheme.implementations)
  37. return implementations
  38. @staticmethod
  39. @lru_cache(maxsize=1)
  40. def all_supported_implementations():
  41. return [impl for impl in Scheme.all_implementations()
  42. if impl.supported_on_current_platform()]
  43. @staticmethod
  44. @lru_cache(maxsize=32)
  45. def all_schemes_of_type(type: str) -> list:
  46. schemes = []
  47. p = os.path.join('..', 'crypto_' + type)
  48. if os.path.isdir(p):
  49. for d in os.listdir(p):
  50. if os.path.isdir(os.path.join(p, d)):
  51. if type == 'kem':
  52. schemes.append(KEM(d))
  53. elif type == 'sign':
  54. schemes.append(Signature(d))
  55. else:
  56. assert('Unknown type')
  57. return schemes
  58. @lru_cache(maxsize=None)
  59. def metadata(self):
  60. metafile = os.path.join(self.path(), 'META.yml')
  61. try:
  62. with open(metafile, encoding='utf-8') as f:
  63. metadata = yaml.safe_load(f)
  64. return metadata
  65. except Exception as e:
  66. print("Can't open {}: {}".format(metafile, e))
  67. return None
  68. def __repr__(self):
  69. return "<{}({})>".format(self.type.title(), self.name)
  70. class Implementation:
  71. def __init__(self, scheme, name):
  72. self.scheme = scheme
  73. self.name = name
  74. @lru_cache(maxsize=None)
  75. def metadata(self):
  76. for i in self.scheme.metadata()['implementations']:
  77. if i['name'] == self.name:
  78. return i
  79. def path(self, base='..') -> str:
  80. return os.path.join(self.scheme.path(base=base), self.name)
  81. def libname(self) -> str:
  82. if os.name == 'nt':
  83. return "lib{}_{}.lib".format(self.scheme.name, self.name)
  84. return "lib{}_{}.a".format(self.scheme.name, self.name)
  85. def cfiles(self) -> [str]:
  86. return glob.glob(os.path.join(self.path(), '*.c'))
  87. def hfiles(self) -> [str]:
  88. return glob.glob(os.path.join(self.path(), '*.h'))
  89. def ofiles(self) -> [str]:
  90. return glob.glob(os.path.join(self.path(),
  91. '*.o' if os.name != 'nt' else '*.obj'))
  92. @staticmethod
  93. @lru_cache(maxsize=None)
  94. def by_name(scheme_name, implementation_name):
  95. scheme = Scheme.by_name(scheme_name)
  96. for implementation in scheme.implementations:
  97. if implementation.name == implementation_name:
  98. return implementation
  99. raise KeyError()
  100. @staticmethod
  101. @lru_cache(maxsize=None)
  102. def all_implementations(scheme: Scheme) -> list:
  103. implementations = []
  104. for d in os.listdir(scheme.path()):
  105. if os.path.isdir(os.path.join(scheme.path(), d)):
  106. implementations.append(Implementation(scheme, d))
  107. return implementations
  108. @staticmethod
  109. def all_supported_implementations(scheme: Scheme) -> list:
  110. return [impl for impl in Implementation.all_implementations(scheme)
  111. if impl.supported_on_current_platform()]
  112. def namespace_prefix(self):
  113. return '{}{}_'.format(self.scheme.namespace_prefix(),
  114. self.name.upper()).replace('-', '')
  115. def supported_on_os(self, os: Optional[str] = None) -> bool:
  116. """Check if we support the OS
  117. If no OS is specified, then we run on the current OS
  118. """
  119. if os is None:
  120. os = platform.system()
  121. for platform_ in self.metadata().get('supported_platforms', []):
  122. if 'operating_systems' in platform_:
  123. if os not in platform_['operating_systems']:
  124. return False
  125. return True
  126. @lru_cache(maxsize=10000)
  127. def supported_on_current_platform(self) -> bool:
  128. if 'supported_platforms' not in self.metadata():
  129. return True
  130. if platform.machine() == 'ppc':
  131. return False
  132. if not self.supported_on_os():
  133. return False
  134. cpuinfo = helpers.get_cpu_info()
  135. for platform_ in self.metadata()['supported_platforms']:
  136. if platform_['architecture'] == cpuinfo['arch'].lower():
  137. # Detect actually running on emulated i386
  138. if (platform_['architecture'] == 'x86_64' and
  139. platform.architecture()[0] == '32bit'):
  140. continue
  141. if all([flag in cpuinfo['flags']
  142. for flag in platform_['required_flags']]):
  143. return True
  144. return False
  145. def __str__(self):
  146. return "{} implementation of {}".format(self.name, self.scheme.name)
  147. def __repr__(self):
  148. return "<Implementation({}, {})>".format(self.scheme.name, self.name)
  149. class KEM(Scheme):
  150. def __init__(self, name: str):
  151. self.type = 'kem'
  152. self.name = name
  153. self.implementations = Implementation.all_implementations(self)
  154. @staticmethod
  155. def all_kems() -> list:
  156. return Scheme.all_schemes_of_type('kem')
  157. class Signature(Scheme):
  158. def __init__(self, name: str):
  159. self.type = 'sign'
  160. self.name = name
  161. self.implementations = Implementation.all_implementations(self)
  162. @staticmethod
  163. def all_sigs():
  164. return Scheme.all_schemes_of_type('sign')