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.
 
 
 

198 regels
6.0 KiB

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